package com.book.web.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * logback的測試 * @author liweihan * */ public class TestLogback { /** 1.引入jar包 <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.7</version> </dependency> 2.logback 官方使用方式,其實就和slf4j集成了起來 下面Logger、LoggerFactory都是slf4j本身的接口與類 沒有配置文件的狀況下,使用的是默認配置。搜尋配置文件的過程以下: 尋找logback.configurationFile的系統屬性 → 尋找 logback.groovy → 尋找 logback-test.xml → 尋找 logback.xml 目前路徑都是定死的(放在類路徑下),只有logback.configurationFile的系統屬性是能夠更改的, 因此若是咱們想更改配置文件的位置(不想放在類路徑下),則須要設置這個系統屬性: System.setProperty("logback.configurationFile","/path/to/config.xml"); 解析完配置文件後,返回的ILoggerFactory實例的類型是LoggerContext(它包含了配置信息) 根據返回的ILoggerFactory實例,來獲取Logger 就是根據上述的LoggerContext來建立一個Logger, 每一個logger與LoggerContext創建了關係,並放到LoggerContext的緩存中 3.若是logback-和commons-logging集成,還須要加入jar包 <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.7</version> </dependency> private static Log logger = LogFactory.getLog(TestLogback.class); logback自己的使用其實就和slf4j綁定了起來,如今要想指定commons-logging的底層log實現是logback,則須要2步走 第一步: 先將commons-logging底層的log實現轉向slf4j (jcl-over-slf4j乾的事) 第二步: 再根據slf4j的選擇底層日誌原理,咱們使之選擇上logback 這樣就能夠完成commons-logging與logback的集成。即寫着commons-logging的API,底層倒是logback來進行輸出 */ private static final Logger logger = LoggerFactory.getLogger(TestLogback.class); public static void main(String[] args) { if (logger.isDebugEnabled()) { logger.debug(" ====== slf4j-logback debug message"); } if (logger.isInfoEnabled()) { logger.info(" ====== slf4j-logback info message"); } if (logger.isTraceEnabled()) { logger.trace(" ====== slf4j-logback trace message"); } if (logger.isWarnEnabled()) { logger.error(" ====== slf4j-logback error message"); } } }
若是咱們不配置logback-test.xml或logback.xml,那麼將要調用logback.configurationFile的系統屬性。html
logback.xml的配置:html5
<?xml version="1.0" encoding="UTF-8"?> <!-- if debug set to true, context info always print otherwise the contex info will print when error occour --> <configuration scan="true" scanPeriod="20 seconds" debug="false"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n </pattern> </encoder> </appender> <!-- 全部日誌文件 --> <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${html5.log.path}html5.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${html5.log.path}html5.%d{yyyy-MM-dd}.log </fileNamePattern> <maxHistory>15</maxHistory> </rollingPolicy> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n </pattern> <charset>UTF-8</charset> </encoder> </appender> <!--2016-07-13-han-add : just for scan error log clearly!--> <appender name="ERROR-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${html5.log.path}error.log</file> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n </pattern> <charset>UTF-8</charset> </encoder> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${html5.log.path}error.%d{yyyy-MM-dd}.log </fileNamePattern> <maxHistory>15</maxHistory> </rollingPolicy> </appender> <!--2016-07-13-add-han-just for test some Class or package <logger name="com.vis.servlet.PhonePlayServlet" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT"/> <appender-ref ref="ROLLING"/> </logger> <logger name="com.vis.servlet.PhoneUGCPlayListServlet" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT"/> <appender-ref ref="ROLLING"/> </logger> <logger name="com.vis.servlet.PhoneUGCWxServlet" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT"/> <appender-ref ref="ROLLING"/> </logger> <logger name="com.vis.servlet.PhoneUGCServlet" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT"/> <appender-ref ref="ROLLING"/> </logger> <logger name="com.vis.servlet.PhoneUGCPlayListWxServlet" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT"/> <appender-ref ref="ROLLING"/> </logger> --> <root level="info"> <appender-ref ref="ROLLING"/> <appender-ref ref="STDOUT"/> <appender-ref ref="ERROR-OUT"/> </root> </configuration>
配置說明:java
1、根節點<configuration>包含的屬性:web
scan:正則表達式
當此屬性設置爲true時,配置文件若是發生改變,將會被從新加載,默認值爲true。算法
scanPeriod:數據庫
設置監測配置文件是否有修改的時間間隔,若是沒有給出時間單位,默認單位是毫秒。api
當scan爲true時,此屬性生效。默認的時間間隔爲1分鐘。
debug:數組
當此屬性設置爲true時,將打印出logback內部日誌信息,實時查看logback運行狀態。默認值爲false。緩存
例子如上!
2、設置logger
<logger> : 用來設置某一個包或者具體的某一個類的日誌打印級別、以及指定<appender>。<logger>僅有一個name屬性,一個可選的level和一個可選的addtivity屬性。
name : 用來指定受此logger約束的某一個包或者具體的某一個類。
level: 用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
還有一個特殊值INHERITED或者同義詞NULL,表明強制執行上級的級別。
若是未設置此屬性,那麼當前loger將會繼承上級的級別。
addtivity: 是否向上級loger傳遞打印信息。默認是true。
<logger>能夠包含零個或多個<appender-ref>元素,
標識這個appender將會添加到這個loger。
<!-- 該包全部類的日誌的打印,可是並沒用設置打印級別, 因此繼承他的上級<root>的日誌級別。 沒有設置addtivity,默認爲true,將此loger的打印信息向上級傳遞; 沒有設置appender,此loger自己不打印任何信息。 --> <logger name="com.book.web.controller"/> <!-- TestLogback類 ,此時該類的日誌就不會輸入到文件中,日誌級別爲INFO additivity設置爲true時,將打印兩第二天志! --> <logger name="com.book.web.controller.TestLogback" level="INFO" additivity="false"> <appender-ref ref="STDOUT"/> </logger>
3、設置root
其實,他也是<logger>元素,可是它是根loger。只有一個level屬性
level:
用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能設置爲INHERITED或者同義詞NULL。默認是DEBUG
<root>能夠包含零個或多個<appender-ref>元素,標識這個appender將會添加到這個logger。
4、設置appender
<appender>:
<appender>是<configuration>的子節點,是負責寫日誌的組件。
<appender>有兩個必要屬性name和class。name指定appender名稱,class指定appender的全限定名。
1.ConsoleAppender:
把日誌添加到控制檯,有如下子節點:
<encoder>:對日誌進行格式化。
<target>:字符串 System.out 或者 System.err ,默認 System.out ;
2.FileAppender:
把日誌添加到文件,有如下子節點:
<file>:被寫入的文件名,能夠是相對目錄,也能夠是絕對目錄,若是上級目錄不存在會自動建立,沒有默認值。<append>:若是是 true,日誌被追加到文件結尾,若是是 false,清空現存文件,默認是true。
<encoder>:對記錄事件進行格式化。(具體參數稍後講解 )
<prudent>:若是是 true,日誌會被安全的寫入文件,即便其餘的FileAppender也在向此文件作寫入操做,效率低,默認是 false。
<configuration> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>testFile.log</file> <append>true</append> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
3.RollingFileAppender:
滾動記錄文件,先將日誌記錄到指定文件,當符合某個條件時,將日誌記錄到其餘文件。有如下子節點:
<file>:被寫入的文件名,能夠是相對目錄,也能夠是絕對目錄,若是上級目錄不存在會自動建立,沒有默認值。<append>:若是是 true,日誌被追加到文件結尾,若是是 false,清空現存文件,默認是true。
<encoder>:對記錄事件進行格式化。(具體參數稍後講解 )
<rollingPolicy>:當發生滾動時,決定 RollingFileAppender 的行爲,涉及文件移動和重命名。
<triggeringPolicy >: 告知 RollingFileAppender 合適激活滾動。
<prudent>:當爲true時,不支持FixedWindowRollingPolicy。支持TimeBasedRollingPolicy,可是有兩個限制,1不支持也不容許文件壓縮,2不能設置file屬性,必須留空。
3.1 rollingPolicy:
3.1.1 TimeBasedRollingPolicy: 最經常使用的滾動策略,它根據時間來制定滾動策略,
既負責滾動也負責出發滾動。有如下子節點:
<fileNamePattern>:
必要節點,包含文件名及「%d」轉換符, 「%d」能夠包含一個java.text.SimpleDateFormat指定的時間格式,如:%d{yyyy-MM}。若是直接使用 %d,默認格式是 yyyy-MM-dd。
RollingFileAppender 的file字節點無關緊要,經過設置file,能夠爲活動文件和歸檔文件指定不一樣位置,當前日誌老是記錄到file指定的文件(活動文件),活動文件的名字不會改變;若是沒設置file,活動文件的名字會根據fileNamePattern 的值,每隔一段時間改變一次。「/」或者「\」會被當作目錄分隔符。
<maxHistory>:
可選節點,控制保留的歸檔文件的最大數量,超出數量就刪除舊文件。假設設置每月滾動,且<maxHistory>是6,則只保存最近6個月的文件,刪除以前的舊文件。注意,刪除舊文件是,那些爲了歸檔而建立的目錄也會被刪除。
3.1.2 FixedWindowRollingPolicy: 根據固定窗口算法重命名文件的滾動策略。有如下子節點:
<minIndex>:窗口索引最小值
<maxIndex>:窗口索引最大值,當用戶指定的窗口過大時,會自動將窗口設置爲12。
<fileNamePattern >:
必須包含「%i」例如,假設最小值和最大值分別爲1和2,命名模式爲 mylog%i.log,會產生歸檔文件mylog1.log和mylog2.log。還能夠指定文件壓縮選項,例如,mylog%i.log.gz 或者 沒有log%i.log.zip
3.2 triggeringPolicy:
SizeBasedTriggeringPolicy: 查看當前活動文件的大小,若是超過指定大小會告知RollingFileAppender 觸發當前活動文件滾動。只有一個節點:
<maxFileSize>:這是活動文件的大小,默認值是10MB。
<!-- 天天生成一個文件,保留15天 --> <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>log/test.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>log/test.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>15</maxHistory> </rollingPolicy> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5level] %logger{50}.%M[%line] - %msg%n</pattern> <charset>UTF-8</charset> </encoder> </appender> <!-- 按照固定窗口模式生成日誌文件,當文件大於20MB時,生成新的日誌文件。 窗口大小是1到3,當保存了3個歸檔文件後,將覆蓋最先的日誌。 --> <appender name="ROLLING2" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>log/html5.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>log/html5.%i.log</fileNamePattern> <minIndex>1</minIndex> <maxIndex>3</maxIndex> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <maxFileSize>20MB</maxFileSize> </triggeringPolicy> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n</pattern> <charset>UTF-8</charset> </encoder> </appender>
5、設置encoder
負責兩件事,一是把日誌信息轉換成字節數組,二是把字節數組寫入到輸出流。
目前PatternLayoutEncoder 是惟一有用的且默認的encoder ,有一個<pattern>節點,用來設置日誌的輸入格式。使用「%」加「轉換符」方式,若是要輸出「%」,則必須用「\」對「\%」進行轉義。
<pattern>裏面的轉換符說明:
轉換符 | 做用 |
---|---|
c {length } lo {length } logger {length } |
輸出日誌的logger名,可有一個×××參數,功能是縮短logger名,設置爲0表示只輸入logger最右邊點符號以後的字符串。Conversion specifierLogger nameResult%loggermainPackage.sub.sample.BarmainPackage.sub.sample.Bar%logger{0}mainPackage.sub.sample.BarBar%logger{5}mainPackage.sub.sample.Barm.s.s.Bar%logger{10}mainPackage.sub.sample.Barm.s.s.Bar%logger{15}mainPackage.sub.sample.Barm.s.sample.Bar%logger{16}mainPackage.sub.sample.Barm.sub.sample.Bar%logger{26}mainPackage.sub.sample.BarmainPackage.sub.sample.Bar
|
C {length } class {length } |
輸出執行記錄請求的調用者的全限定名。參數與上面的同樣。儘可能避免使用,除非執行速度不形成任何問題。 |
contextName cn |
輸出上下文名稱。 |
d {pattern } date {pattern } |
輸出日誌的打印日誌,模式語法與java.text.SimpleDateFormat 兼容。Conversion PatternResult%d2006-10-20 14:06:49,812%date2006-10-20 14:06:49,812%date{ISO8601}2006-10-20 14:06:49,812%date{HH:mm:ss.SSS}14:06:49.812%date{dd MMM yyyy ;HH:mm:ss.SSS}20 oct. 2006;14:06:49.812 |
F / file | 輸出執行記錄請求的java源文件名。儘可能避免使用,除非執行速度不形成任何問題。 |
caller{depth}caller{depth, evaluator-1, ... evaluator-n} | 輸出生成日誌的調用者的位置信息,整數選項表示輸出信息深度。 例如, %caller{2} 輸出爲: 0 [main] DEBUG - logging statement Caller+0 at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22) Caller+1 at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17) 例如, %caller{3} 輸出爲: 16 [main] DEBUG - logging statement Caller+0 at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22) Caller+1 at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17) Caller+2 at mainPackage.ConfigTester.main(ConfigTester.java:38) |
L / line | 輸出執行日誌請求的行號。儘可能避免使用,除非執行速度不形成任何問題。 |
m / msg / message | 輸出應用程序提供的信息。 |
M / method | 輸出執行日誌請求的方法名。儘可能避免使用,除非執行速度不形成任何問題。 |
n | 輸出平臺先關的分行符「\n」或者「\r\n」。 |
p / le / level | 輸出日誌級別。 |
r / relative | 輸出從程序啓動到建立日誌記錄的時間,單位是毫秒 |
t / thread | 輸出產生日誌的線程名。 |
replace(p ){r, t} | p 爲日誌內容,r 是正則表達式,將p 中符合r 的內容替換爲t 。 例如, "%replace(%msg){'\s', ''}" |
我本身用的幾個
<encoder> <!-- --> <pattern>%d{HH:mm:ss.SSS} [%thread] [%-5level] %logger{50}.%M[%line] - %msg%n</pattern> <!-- 本地調試用 <pattern>%d{yyyy/MM/dd-HH:mm:ss} [%level] [%thread] %caller{1}-%msg%n</pattern> --> <!-- <Pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} [ %-5level ][ %thread ] %logger{56}.%method[%L] - %msg%n</Pattern> --> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder>
格式修飾符,與轉換符共同使用:
可選的格式修飾符位於「%」和轉換符之間。
第一個可選修飾符是左對齊 標誌,符號是減號「-」;接着是可選的最小寬度 修飾符,用十進制數表示。若是字符小於最小寬度,則左填充或右填充,默認是左填充(即右對齊),填充符爲空格。若是字符大於最小寬度,字符永遠不會被截斷。最大寬度 修飾符,符號是點號"."後面加十進制數。若是字符大於最大寬度,則從前面截斷。點符號「.」後面加減號「-」在加數字,表示從尾部截斷。
例如:%-4relative 表示,將輸出從程序啓動到建立日誌記錄的時間 進行左對齊 且最小寬度爲4。
6、設置Filter
<filter>:過濾器,執行一個過濾器會有返回1個枚舉值,即DENY,NEUTRAL,ACCEPT其中之一。
返回DENY,日誌將當即被拋棄再也不通過其餘過濾器;
返回NEUTRAL,有序列表裏的下個過濾器過接着處理日誌;
返回ACCEPT,日誌會被當即處理,再也不通過剩餘過濾器。
過濾器被添加到<Appender> 中,爲<Appender> 添加一個或多個過濾器後,能夠用任意條件對日誌進行過濾。<Appender> 有多個過濾器時,按照配置順序執行。
LevelFilter: 級別過濾器,根據日誌級別進行過濾。若是日誌級別等於配置級別,過濾器會根據onMath 和 onMismatch接收或拒絕日誌。有如下子節點:
<level>:設置過濾級別
<onMatch>:用於配置符合過濾條件的操做
<onMismatch>:用於配置不符合過濾條件的操做
例如:將過濾器的日誌級別配置爲INFO,全部INFO級別的日誌交給appender處理,非INFO級別的日誌,被過濾掉。
<configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder> <pattern> %-4relative [%thread] %-5level %logger{30} - %msg%n </pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="CONSOLE" /> </root> </configuration>
例子2:將錯誤日誌單獨到一個文件error.log中
<configuration> <appender name="ERROR-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>log/error.log</file> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n </pattern> <charset>UTF-8</charset> </encoder> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>log/error.%d{yyyy-MM-dd}.log </fileNamePattern> <maxHistory>15</maxHistory> </rollingPolicy> </appender> <root level="DEBUG"> <appender-ref ref="ERROR-OUT"/> </root> </configuration>
例子3:當咱們須要按時間滾動日誌,同時又須要按照大小滾動時:
<configuration> <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>mylog.txt</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!-- rollover daily --> <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern> <!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB --> <maxFileSize>100MB</maxFileSize> <maxHistory>60</maxHistory> <totalSizeCap>20GB</totalSizeCap> </rollingPolicy> <encoder> <pattern>%msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="ROLLING" /> </root> </configuration>
1、
Logback是由log4j創始人Ceki Gülcü設計的又一個開源日誌組件。Logback,一個「可靠、通用、快速而又靈活的Java日誌框架」。logback當前分紅三個模塊:logback-core,logback- classic和logback-access。
logback-core是其它兩個模塊的基礎模塊。
logback-classic是log4j的一個改良版本。
logback-classic完整實現SLF4J API使你能夠很方便地更換成其它日誌系統如log4j或JDK Logging。logback-access訪問模塊與Servlet容器集成提供經過Http來訪問日誌的功能。
2、
Logback主要創建於Logger、Appender 和 Layout 這三個類之上。
1.
Logger:日誌的記錄器,把它關聯到應用的對應的context上後,主要用於存放日誌對象,也能夠定義日誌類型、級別。Logger對象通常多定義爲靜態常量.
2.
Appender:用於指定日誌輸出的目的地,目的地能夠是控制檯、文件、遠程套接字服務器、 MySQL、 PostreSQL、Oracle和其餘數據庫、 JMS和遠程UNIX Syslog守護進程等。
3.
Layout:負責把事件轉換成字符串,格式化的日誌信息的輸出。
3、
Level 有效級別
Logger能夠被分配級別。級別包括:TRACE、DEBUG、INFO、WARN和ERROR,定義於ch.qos.logback.classic.Level類。程序會打印高於或等於所設置級別的日誌,設置的日誌等級越高,打印出來的日誌就越少。若是設置級別爲INFO,則優先級高於等於INFO級別(如:INFO、 WARN、ERROR)的日誌信息將能夠被輸出,小於該級別的如DEBUG將不會被輸出。爲確保全部logger都可以最終繼承一個級別,根logger老是有級別,默認狀況下,這個級別是DEBUG
4、
三值邏輯
Logback的過濾器基於三值邏輯(ternary logic),容許把它們組裝或成鏈,從而組成任意的複合過濾策略。過濾器很大程度上受到Linux的iptables啓發。這裏的所謂三值邏輯是說,過濾器的返回值只能是ACCEPT、DENY和NEUTRAL的其中一個。
若是返回DENY,那麼記錄事件當即被拋棄,再也不通過剩餘過濾器;
若是返回NEUTRAL,那麼有序列表裏的下一個過濾器會接着處理記錄事件;
若是返回ACCEPT,那麼記錄事件被當即處理,再也不通過剩餘過濾器。
5、
Filter 過濾器
Logback-classic提供兩種類型的過濾器:常規過濾器和TuroboFilter過濾器。Logback總體流程:
Logger 產生日誌信息;
Layout修飾這條msg的顯示格式;
Filter過濾顯示的內容;
Appender具體的顯示,即保存這日誌信息的地方。
6、參考文章
配置和使用
http://blog.csdn.net/haidage/article/details/6794509/
http://blog.csdn.net/haidage/article/details/6794529
http://blog.csdn.net/haidage/article/details/6794540
http://blog.csdn.net/canot/article/details/53340295
http://www.cnblogs.com/wenbronk/p/6529161.html
http://blog.csdn.net/iamlihongwei/article/details/56283138
http://www.cnblogs.com/chenfeng1122/p/6179376.html
//性能對比
https://my.oschina.net/OutOfMemory/blog/78926七、
http://blog.csdn.net/yjh1271845364/article/details/7088826二、
//原理