
2 数据模型
a) 类似目录的变量称为hashes,包含保存下级变量的唯一的查询名字,充当其它对象的容器,每个都关联唯一的查询名字
b) 类似文件的变量称为scalars,保存单值,有两种类型
i 字符串:用引号括起,单引号or双引号
ii 数字:不用引号
iii 日期:可以是日期,时间或日期-时间
iv 布尔值:true或false,通常在<#if …>标记中使用
v 对scalars的访问从root开始,各部分用””分隔,如 animalsmouseprice
c) 另外一种变量是sequences,和hashes类似,充当其它对象的容器,按次序访问,只是不使用变量名字,而使用数字索引,如animals[0]name,索引值从0开始
d) 通常每个变量具有上述的一种能力,但一个变量可以具有多种上述能力
e) 方法
i ${avg(3,5,20)} ${avg(studentzhangyaangage,studentsituage)}
f) 宏和变换器:用户自定义的指令(FTL标记)
g) 节点:树形结构中的一个节点,常用于xml处理中
3 模板
a) 文本:直接输出
b) ${…}:称为interpolations,FreeMarker会在输出时用实际值代替,或#{},只用于文本中
c) FTL标记:类似于HTML,为了与HTML区分,用#开始,有的以@开始,不会输出,区分大小写,FTL标记不能位于另一个FTL标记内部
d) 注释:<#--和-->,不会输出
e) 多余的空白字符会在模板输出时移除
f) 指令
i 使用FTL标记引用指令,有三种FTL标记
1 开始标记:<#name prarm>
2 结束标记:</#name>
3 空内容指令标记:<#name param />
ii 有两种类型的指令:预定义指令和用户定义指令,用户定义指令要用@替换#
iii FTL标记不能够交叉,应该正确嵌套
iv 如果使用不存在的指令,FreeMarker会产生错误消息
v < , </ 和指令间不允许有空白字符
g) 表达式
i 字符串
1 使用单引号或双引号限定
2 特殊字符要转义
3 raw字符串,可以认为是文本,其中的$和{等不具有特殊含义,该类字符串在引号前面加r,如${r”${foo}”} ${r”c:foobar”}
ii 数字
1 直接输入,不需要引号
2 精度数字使用””分隔,不能使用分组符号
3 不支持科学计数法
4 不能省略小数点前面的0
5 数字8 ,+8 ,08和800 都是相同的
iii 布尔值:true 和 false, 不使用引号
iv 序列
1 有逗号分隔的子变量列表,有方括号限定,如:
<#list [“winter”, ”spring”, ”summer”, ”autumn”] as x>
${x}
</#list>
2 列表的项目是表达式,如:
[2+2, [1,2,3,4], ”whatnot”]
3 可以使用数字范围定义数字序列,如:
25等同于[2,3,4,5] 注意:数字范围没有方括号 如:52
4 散列(hash) 由逗号的键/值列表,有大括号限定,键和值之间用冒号分隔
{“name”:”zhangyang”,”age”:23} 键和值都是表达式,但键必须是字符串
v 获取变量
1 顶层变量:${variable}, 变量名只能是字母, 数字, 下划线, $, @ 和# 的组合, 不能以数字开头
2 从散列中获取数据
a) schoolstudentname
b) school[“student”]name
c) school[“student”][“name”]
说明:使用点语法,变量名有顶层变量一样的限制, 方括号语法没有该限制
3 从序列获(sequences)得数据: 和散列的方括号语法一样, 只是方括号的表达式值必须是数字; 注意: 第一个项目的索引是0
4 序列片段: 使用[startIndexendIndex]语法, 从序列中获得片段(也是序列)
5 特殊变量: FreeMarker内定义的变量, 使用 variablenae 语法访问
vi 字符串 *** 作
1 Interpolation(或连接 *** 作)
a) 可以使用${} 或#{} 在文本部分插入表达式的值,
如${“Hello ${user}”} 等于 ${“Hello”+user}
b) ${} 只能用于文本部分, 不用于FTL表达式中
2 子串
假设user的值为”tom cat”
${user[0]}${user[4]} tc
${user[14]} om c
vii 序列 *** 作
连接 *** 作: 和字符串一样, 用”+”
<#list [“joe”,”fred”] + [“julia”,”kate”] as user>
${user}
</#list>
viii 散列 *** 作
连接 *** 作: 和字符串一样, 用”+”, 如果有相同的key, 右边的值替换左边的值(后面的替换前面的), 如
<#assign ages = {“joe”:23,”fred”:25} + {“joe”:30,”julia”:23}>
</#assign> 结果: joe的值为30
ix 算术运算
1 +, -- , , /, %
${x / 2}
2 *** 作符两边必须是数字
3 使用 “+” *** 作符时,如果一边是数字, 一边是字符串, 就会自动将数字转换为字符串, 如: ${3+”4”} 结果 34
4 使用内建的 int 获得整数部分
${11int} 1
${1999int} 1
${(5/2)int} 2
5 比较 *** 作符
a) 使用=(或==,完全相等)测试两个值是否相等, 使用!=测试两个值是否不相等
b) = 和!= 两边必须是相同类型的值
c) FreeMarker是精确比较,”x”,”x “和”x”是不等的
d) 对数字和日期可以使用<, <=, >, 和>=, 但不用用于字符串
e) 由于FreeMarker 会将>解释成FTL标记的结束字符, 所以对于>和>=可以使用括号来避免这种情况, 如<#if (x > y)>
f) e)的一种替代方法, 使用lt, lte, gt 和gte来替代
6 逻辑 *** 作符
a) &&(and), ||(or), !(not), 只能用于布尔值
<#if x < 13 && color = “green”>
…
</#if>
<#if ! man>
woman
</#if>
7 内建函数
a) 内建函数的用法类似访问散列的子变量, 只是用””|替代”,”
b) 用于字符串
i html: 对字符串进行html编码
ii cap_first: 将字符串转换成第一个字母大写
iii lower_case: 小写
iv upper_case: 大写
v trim: 去掉字符串前后的空白字符
c) 用于序列
i size: 获得序列中元素的数目
d) 用于数字
i int: 取得数字的整数部分
e) 例子:
test = “tom & jerry”
${testhtml} tom & jerry
${testupper_casehtml} TOM & JERRY
8 *** 作符优先顺序
h) Interpolation(只用于文本部分)
i Interpolation有两种类型
1 通用Interpolation: ${expr}
a) 插入字符串值: 直接输出表达式结果
b) 插入数字值: 根据缺省格式(由#setting指令设置)将表达式转换成文本输出; 可以使用内建函数string格式化单个Interpolation
c) 例子
<#setting number_format=”currency”/> 金额
<#assign answer=43/>
${answer} $4300
${answerstring} $4300
${answerstringnumber} 43
${answerstringcurrency} $4300
${answerstringpercent} 4,300%
2 数字Interpolation: ${expr} 或#{expr ; format}
3 插入日期: 根据缺省格式(#setting) 将表达式结果转换成文本输出; 可以使用内建的函数string 格式化单个Interpolation, 例子:
${updatestring(“yyyy-MM-dd HH:mm:ss zzzz”)}
4 插入布尔值: 说明同上
<#assign foo = true/>
${foostring(“yes”,”no”)} yes
5 数字Interpolation的#{expr ; format}形式可以用来格式化数字, format可以是
a) mX: 小数部分最小X位
b) MX: 小数部分最大X位
c) 例子
<#assign x=2364/>
<#assign y=4/>
#{x; M2} 236
#{y; M2} 4
#{x; m1} 24
#{y; m1} 40
i) 例子
i if指令
<#if zhangyangage < situage>
Situ is order
<#else>
Zhangyang is order
</#if>
ii list指令 <#list students[05] as s_list></#list>取list的前5条记录
<table border = “1”>
<tr>
<td>name</td><td>age</td>
<#list students as s_list>
<td>${s_listname}</td><td>${s_listage}</td>
</#list>
</tr>
</table>
iii include指令
<#include “/all/headerhtml”>
…
<#include “/all/footerhtml”>
iv 应用
<table>
<#list students as s_list>
<tr>
<td>
<#if s_listsex =”man”><img src=”/img/boygif”/>
<#else><img src=”/img/girlgif/>
</#if>
${s_listname}
</td>
<td>${s_listage}</td>
</tr>
</#list>
</table>ftl不可以和jsp共存的,二者具有转化关系。
freemarker(FTL)常见语法(JSP中的代码转化为ftl代码) 举例如下:
1、引入公共页面<#include "paramsftl">
2、判断是否为空:<#if (resultContextpaginationresultList) && resultContextpaginationresultListsize gt 0>
3、集合遍历:
<#list list as a>
${a_index+1}
</#list>
4、访问Java类中的静态方法
<#if st == stackfindValue("@comtestboSkuTag@NEW")><#elseif st == stackfindValue("@comtestboSkuTag@SPECIAL") >
5、访问Request对象
${RequesteffectTypeinfoName!'' } 其他域对象比如:Session对象 貌似还是是${Session}
6、使用其他标签如struts2标签:
引入tld:<#assign ts=JspTaglibs["/skw-tags"] /> 使用: <@tsproductPrice loginUrl="${ctxtPath}/login_jsphtml"/>
FTL指Flash Translation Layer(闪存转换层),这玩意儿的本职工作是完成Host的逻辑地址到Flash的物理地址的转换。
controller每把一笔数据写入Flash中时,便会记录下该数据的逻辑地址到物理地址的映射关系,这样当Host想要读取这笔数据时就会根据这个映射读取到真实的数据。
首先说明一下闪存的特性:
针对闪存的以上特性,FTL在完成本职工作逻辑地址到物理地址转换的同时,还要具有十八般武艺:
垃圾回收(Garbage Collection),磨损平衡(Ware Leveling),坏块管理,读干扰处理,数据保持处理等事情。
映射有基于块映射、基于页映射和混合映射。
基于块映射 Host在读写小尺寸数据(随机性能)时性能较差,一般用在U盘中(ps:不要惊讶,U盘中也用到FTL的),所以U盘不适合装系统,适合用来读写大尺寸数据的数据传输用。
基于页映射 对小尺寸数据(随机性能)有很好的性能,但是由于闪存的页要比闪存块多的多,需要更多的空间来存放映射表。为了追求更好的随机性能( *** 作系统对这个很在意)所以SSD都采用页映射。
混合映射 的性能介于上边二者之间,当然存放映射表所用的空间也介于二者之间。
SSD内部FW维护了一张逻辑地址到物理地址的映射表(Map Table)。用户每写入一个逻辑页就需要更改一次MT;当读取一个逻辑页时,SSD会查找映射表中该逻辑页对应的物理页,然后再访问Flash读取所要的数据。
一般映射的逻辑页的大小为4KB,物理地址的大小为4Byte,那么一个256GB的SSD的映射表大小为: 256GB/4KB4B = 256MB ,也就是说映射表的大小是SSD容量的1/1024。
为了存放这个映射表,SSD一般有两种设计方案:DRAM(带DRAM的)和DRAM-less(不带DRAM的)。带DRAM的一般会把缓冲数据和映射表统统存放在DRAM中,优点是查找更新映射表迅速,性能较好,劣势是多了一个DRAM,成本和功耗上升,当前的主流SSD是该方案;DRAM-less的会将少部分的映射表放在片内的SRAM上,其余部分的映射表则会放在Flash中,这种方案好处是节省了DRAM的成本和功耗,但是读写Flash要比读写DRAM要慢得多,所以速度上不如DRAM方案,性能较差,当前入门级的SSD多采用这种方案。
一个小问题:我们都知道DRAM掉电数据是会丢失的,那么数据表怎么办?
答案是在SSD掉电之前,它会把映射表写入到Flash中去的,下次上电初始化时需要重新把映射表读出来放在DRAM或SRAM中。也就是说不管DRAM或DRAM-less,Flash中都需要储存映射表的。
这年头收垃圾也成特色技能了,还是重要内功。。。哈哈
垃圾回收,就是把某一个闪存块上的有效数据读出来,写入另一个数据块中,然后擦除原来的闪存块,使之可用。
也许你会问,数据在原来的闪存块上待的好好的,为什么要费事吧连的读出来写入另外的块中呢?原因是原来闪存块上的垃圾(无效数据)太多了!如前文讲道,闪存块需要先擦除才能写入,不能覆盖写。如果一个块上只有一点点有效数据占着整个数据块,那么就需要把有效数据搬移出来,把这个块擦除掉使之变成可用状态,从而提高块的使用率。
也许你又会问,为啥子数据会无效呢?原因还是因为闪存的特性导致的,为了延长每个闪存块的寿命,就需要均衡每个数据块的擦除次数,为了均衡每个数据块的擦除次数,数据写入时候就需要均衡的写入每个块,而不能照着一个块往死了写。当用户空间里的数据写满后,就需要把数据写备用空间(SSD的实际容量都比标称容量大,多出来的部分就是备用空间),因为备用空间对 *** 作系统是不可见的,那么每往备用空间写一笔数据就会在用户空间产生一笔无效数据(垃圾)。
江湖传言:SSD越用速度越慢。很不幸,这是真的,而且是有依据的。新盘没有垃圾可收,所以速度快!用久了产生的垃圾就多了,就需要用内功把垃圾逼出来,当然速度就慢下来了。
其实在垃圾回收的时候已经提到磨损平衡的原因了:为了延长每个闪存块的寿命,就需要均衡每个数据块的擦除次数,为了均衡每个数据块的擦除次数,数据写入时候就需要均衡的写入每个块,而不能照着一个块往死了写。
那么,一个闪存块的寿命有多长呢?
SLC:10万次
MLC:1~10千次
TLC:几百次~2千次
QLC:< TLC
3D:< QLC
所以,磨损平衡很重要!接下来几个概念:
冷数据(Cold Data),热数据(Hot Data)
年老块(Old Block),年轻块(Young Block)
所谓 冷数据 就是用户不常更新的数据,比如音视频,只读数据等;相反 热数据 就是频繁更新的数据,比如软件使用等,会产生很多垃圾。
所谓 年老块 是指擦写次数比较多的块;相反 年轻块 则指擦写次数比较少的块。ps:每个块都有EC(Erase Count),年轻还是年老SSD一下就可以看出来。
SSD一般有两种(Ware Leveling)算法:动态磨损平衡(Dynamic WL)和静态磨损平衡(Static WL)。动态磨损平衡算法基本原理是把热数据写到年轻块上;静态磨损平衡的基本思想是把冷数据写到年老块上。这里可以停下来先品味一下这样的做的原因。
DWL好理解:写数据时找年轻的块写,避免往老年块上写数据,各个块就能保持一个比较均衡的值。
SWL呢?冷数据基本不更新,它所占用的块擦写次数就不会增加,而其他块会被经常写入数据,块擦写次数是会增加,这样就导致了 擦除不均衡 ,SSD最不喜欢这个了。所以需要把冷数据搬到老年块上,让年轻块替代老年块的工作。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)