slf4j、logback的使用

使用slf4j配合log4j打日志已经使用很长时间了,已经习惯了log4j的properties文件配置了。 具体参考:《slf4j、log4j 的使用》 虽然从log4j换到logback也不费劲,性能上提升在常规业务上也不是多么明显,但是作为处女座的我,还是想从log4j换到logback来。logback log4j 和slf4j都是同一作者。根据作者宣称的: slf4j的出现是为了替代commons-logging logback的出现是为了替代log4j,用作者的话,叫:“picking up where log4j leaves off" logback比log4j会有更高的性能,和更多的功能支持。 如果你之前是使用slf4j+log4j的组合打日志的话,你只要换个jar包和配置文件就可以了。 之前需要的jar包是: <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency> 现在需要的jar包是: <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.2</version> </dependency> logback-classic的pom会自动引入logback-core包 一般都是sl4j和log4j一起使用的,很少有人直接用log4j的logger打日志。如果你已经那样做了,也不必要修改代码。 只要引入log4j-over-slf4j的桥接包,删掉log4j包,以前log4j打印的日志,会自动桥接到slf4j进而使用logback的logger打印日志。 <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.7.7</version> </dependency> 配置文件使用XML格式配置:logback.xml 基本的示例: <!--60秒检查一次配置文件是否有修改,即时生效--> <configuration scan="true" scanPeriod="60 seconds" debug="false"> <property name="ROOT_LOG_PATH" value="/disk/log" /> <!--设置变量--> <appender name="STDOUT"> <!-- 打印到控制台的appender --> <encoder> <pattern>%d{HH:mm:ss.SSS}%-5level %logger{36}-%msg%n</pattern> </encoder> </appender> <appender name="FILE"> <!--打印到文件,20M滚一个,最多20个--> <file>test.log</file> <rollingPolicy> <fileNamePattern>${ROOT_LOG_PATH}/my.log.%i</fileNamePattern> <maxIndex>20</maxIndex> </rollingPolicy> <triggeringPolicy> <maxFileSize>20MB</maxFileSize> </triggeringPolicy> <encoder> <pattern>%d{HH:mm:ss.SSS}%-5level %logger{36}-%msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <!--根日志--> <appender-ref ref="FILE" /> </root> <!--设置包com.zhaoyanblog的日志级别, 设置是否也打印一份到它的父logger(这里是root)设置的appender里--> <logger name="com.zhaoyanblog" level="INFO" additivity="false"> <appender-ref ref="STDOUT"/> </logger> <!--设置包com.zhaoyanblog.test的日志级别--> <logger name="com.zhaoyanblog.test" level="ERROR" /> </configuration> 以上是对logback的简单配置,平常足够使用了,如果你已经习惯了log4j的properties形式的配置文件,想使用更高效的logback,又不想去学习logback的配置形式。logback为你这种懒人提供了一个log4j.properties在线自动转logback.xml的页面: http://logback.qos.ch/translator/

2015年1月3日 · 1 分钟

[翻译]G1垃圾收集器(八) 之 总结

在本教程,你已经对java虚拟机中的G1垃圾收集器有了大概的了解,首先你学习了堆内存以及GC为什么是java虚拟机里的关键组成部分。接下来你学习了CMS GC和 G1 GC两款GC是如何工作的。后面又学习了G1的命令行参数,和使用它们进行最佳实践。最终你学习了GC日志中包含的日志和数据有关的信息。 在本教程,你学到了: java JVM的组成 G1 GC的概述 回顾了CMS收集器 回顾了G1收集器 命令行参数和最佳实践 关于G1的日志 资源 获取更多的信息或者相关的知识,可以访问以下站点或者链接 Java HotSpot VM Options The Garbage First(G1) Garbage Collector Poonam Bajaj G1 GC Blog Post Java SE 7: Develop Rich Client Applications Java Performance - Charlie Hunt and Binu John Oracle Learning Library 感谢 课程开发者:Michael J Williams 校正:Krishnanjani Chitta

2014年7月14日 · 1 分钟

[翻译]G1垃圾收集器(七) 之 G1 GC记录日志

最后一个主题,我们主要介绍使用日志信息分析G1 GC的性能。本节提供了在日志中打印关于垃圾收集的数据和信息的开关选项。 设置日志详情 有可以设置日志详情到三种不同的级别 (1) -verbosegc (等同于 -XX:+PrintGC) 设置日志到 fine 级别. 例子: [GC pause (G1 Humongous Allocation) (young) (initial-mark) 24M- >21M(64M), 0.2349730 secs] [GC pause (G1 Evacuation Pause) (mixed) 66M->21M(236M), 0.1625268 secs] (2) -XX:+PrintGCDetails 设置为 finer 级别. 这个选项输出下面的信息 平均、最大和最小的暂停时间 根区域扫描, RSet 刷新(包括处理缓存信息), RSet扫描, 对象复制, 消亡(包括尝试的次数). 还包括其它操作使用的时间,例如CSet的选择, 引用的处理, 引用队列的入列,还有Cset的释放 显示Eden, Survivors区域以及总堆内存的占用情况 例子: Ext Root Scanning (ms): Avg: 1.7 Min: 0.0 Max: 3.7 Diff: 3.7] [Eden: 818M(818M)->0B(714M) Survivors: 0B->104M Heap: 836M(4096M)->409M(4096M)] (3) -XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest 设置到finest级别,内容就行finer级别的一样,再加上每个工作线程的信息 例子: ...

2014年7月10日 · 3 分钟

[翻译]G1垃圾收集器(六) 之 命令行选项和最佳实践

命令行选项和最佳实践 在这个章节,让我们一起看看G1的每个命令行选项。 基本的命令行选项 启用G1 GC,使用选项:-XX:+UseG1GC 这是一个最简单的命令行,来启动下载的JDK自带的demos和例子中的Java2Demo java -Xmx50m -Xms50m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar c:\javademos\demo\jfc\Java2D\Java2demo.jar 关键的命令行开关 -XX:+UseG1GC - 告诉JVM,使用G1 GC -XX:MaxGCPauseMillis=200 - 设置一个目标最大暂停时间. 这是一个理想化的目标,JVM会尽可能的完成它. 因此,这个目标不一定能达到。默认是200ms。. -XX:InitiatingHeapOccupancyPercent=45 - 整个堆栈使用达到百分之多少的时候,启动GC周期. 基于整个堆,不仅仅是其中的某个代的占用情况,G1根据这个值来判断是否要触发GC周期, 0表示一直都在GC,默认值是45(即45%慢了,或者说占用了) 最佳实践 在使用G1的时候,下面几个最佳实践你需要遵循 不要设置年轻代大小 显式的使用-Xmn设置年轻代的大小,会干预G1的默认行为。 G1就不会再考虑设定的暂停时间目标,所以本质上说,设定了年轻代大小就相当于禁用了目标暂停时间 G1就无法根据需要增大或者缩小年轻代的小心。既然大小固定了,就无法在大小上做任何改变了。 响应时间指标 不要根据平均响应时间(ART)作为衡量标准去设定XX:MaxGCPauseMillis=<N>选项,而是设定一个想在90%或者以上的时间都会满足这目标的值。也就是说90%的用户,都会在目标时间,甚至更短的时间内得到响应。记住设定的目标时间只是一个目标,不能保证永久都会满足这个目标。 什么是释放(撤销?)失败? 一个JVM当在回收存活或者晋升对象的时候,栈区域溢出了就会发生失败,因为堆的使用已经到达了最大值,不能再扩展。使用-XX:+PrintGCDetails选项,你会看到to-space overflow的日志。这个过程代价很高的。 GC还必须继续对内存进行释放 拷贝不成功的对象继续留在原来的区域 对CSets中对区域的RSets任何更新都要重新生成 所有这些过程代价都是很高的 怎么避免释放(撤销?)失败? 为了避免放失败,考虑使用下面的选项: 增大堆(内存)大小 增大-XX:G1ReservePercent=n,默认是10 G1会预留一部分内存,制造一个假天花板,防止to-space的方式。 早点启动标记周期 增大并行标记的线程数,使用-XX:ConcGCThreads=n选项 G1 GC的开关选项完全列表 下面是G1 GC的所有的开关选项的列表,记得通过上面提到的最佳实践: 选项和默认值 描述 -XX:+UseG1GC 使用G1 GC -XX:MaxGCPauseMillis=n 设置一个暂停时间期望目标,这是一个软目标,JVM会近可能的保证这个目标 -XX:InitiatingHeapOccupancyPercent=n 内存占用达到整个堆百分之多少的时候开启一个GC周期,G1 GC会根据整个栈的占用,而不是某个代的占用情况去触发一个并发GC周期,0表示一直在GC,默认值是45 -XX:NewRatio=n 年轻代和老年代大小的比例,默认是2 -XX:SurvivorRatio=n eden和survivor区域空间大小的比例,默认是8 -XX:MaxTenuringThreshold=n 晋升的阈值,默认是15(译者注:一个存活对象经历多少次GC周期之后晋升到老年代) -XX:ParallelGCThreads=n GC在并行处理阶段试验多少个线程,默认值和平台有关。(译者注:和程序一起跑的时候,使用多少个线程) -XX:ConcGCThreads=n 并发收集的时候使用多少个线程,默认值和平台有关。(译者注:stop-the-world的时候,并发处理的时候使用多少个线程) -XX:G1ReservePercent=n 预留多少内存,防止晋升失败的情况,默认值是10 -XX:G1HeapRegionSize=n G1 GC的堆内存会分割成均匀大小的区域,这个值设置每个划分区域的大小,这个值的默认值是根据堆的大小决定的。最小值是1Mb,最大值是32Mb

2014年7月9日 · 1 分钟

[翻译]G1垃圾收集器(五) 之 G1 GC的步骤(下)

G1的老年代GC 像CMS收集器一样,在老年代的GC上,G1 GC被设计成一个低暂停的收集器。下面的表格描述了G1 GC在老年代上收集的各个阶段。 G1收集阶段-并行标记阶段周期 G1老年代收集的各个阶段如下,请注意有些阶段也是年轻代GC的一部分。 阶段 描述 初始化标记(stop_the_world事件) 这是一个stop_the_world的过程,是随着年轻代GC做的,标记survivor区域(根区域),这些区域可能含有对老年代对象的引用。 根区域扫描 扫描survivor区域中对老年代的引用,这个过程和应用程序一起执行的,这个阶段必须在年轻代GC发生之前完成。 并发标记 查找整个堆中存活的对象,这也是和应用程序一起执行的。这个阶段可以被年轻代的垃圾收集打断。 重新标记(stop-the-world事件) 完成堆内存活对象的标记。使用了一个叫开始前快照snapshot-at-the-beginning (SATB)的算法,这个会比CMS collector使用的算法快。 清理(stop-the-world事件,并且是并发的) 对存活的对象和完全空的区域进行统计(stop-the-world) 刷新Remembered Sets(stop-the-world) 重置空的区域,把他们放到free列表(并发)(译者注:大体意思就是统计下哪些区域又空了,可以拿去重新分配了) 复制(stop-the-world事件) 这个stop-the-world的阶段是来移动和复制存活对象到一个未被使用的区域,这个可以是年轻代区域,打日志的话就标记为 [GC pause (young)]。或者老年代和年轻代都用到了,打日志就会标记为[GC Pause (mixed)]。 G1老年代手机的详细步骤 根据上面的阶段定义,来让我们看看他们都是和老年代怎么交互的。 6、初始化标记阶段 初始化标记,是在年轻代垃圾回收的过程中发生的,在日志中标记为GC pause (young)(inital-mark). 7、并发标记阶段 如果空的区域发现(图中标记为“X”),他们就会在重新标记阶段被立即清除掉,同时决定活跃度的统计信息也会计算到。 8、重新标记 空的区域被删除回收,同时计算所有区域的区域活跃度 9、复制和清理阶段 G1 GC会选择活跃度最低的区域进行收集,因为这些区域的垃圾收集会更快一点,在收集的过程中,年轻代的GC也可能同时发生,所以在GC的日志中标记为 [GC pause (mixed)],意思就是年轻代的GC和老年代的GC可以同时发生。 10、复制和清理阶段之后 那些选择收集的区域,存活对象会被压缩到图中标注的深蓝色区域和深绿色区域。 ...

2014年7月8日 · 1 分钟

[翻译]G1垃圾收集器(五) 之 G1 GC的步骤(上)

G1 GC采用不同的方式分配内存,下面这些图片用来解释G1系统的原理 1、G1的堆结构 堆是一块内存区域分成许多固定大小的区域 区域的大小由JVM启动的时候决定的,一般在1-32M范围内,大约生成2000个左右。 2、G1堆的分配 事实上,这些区域在逻辑上被映射到三大区:Eden, Survivor, 和老年代 图片中的颜色表示了区域和它被赋予的角色,存活的对象被腾挪(复制或者拷贝),从一个区域到另一个区域。区域中的收集被设计成和应用程序并行的。 如图所示,区域可以被分配为Eden, Survivor, 和老年代区域,此外还有第四种,我们称之为Humongous区域。这些区域被设计为保存一个标准区域大于50%的对象。他们被存储为一块连续的区域。这种类型的区域必须是没有被使用的区域(译者注:翻译到这里,暂且不明白,接着往下读) 注:在写这篇文章的时候,对Humongous对象的收集还没得到优化,你应该避免创建这样打的对象。(译者注:更云里雾里了,这个Humongous是指一个区域的类型,还是一些对象的类型呢?) 3、G1中的年轻代 堆被分成接近2000块区域,最小1Mb,最大32Mb。蓝色区域存储老年代对象,绿色区域存储年轻代对象。 注:这些区域不需要像老的GC那样必须是连续的 4、G1中年轻代的收集 存活的对象被复制或者移动到一个或多个survivor区域,如果满足老化阈值,这些对象就会被晋升到老年代(译者注:老化、晋升的概念就是说,一个对象在经历n个周期的收集仍然存活,这个对象就是可能被程序长期持有不愿释放的对象,这个对象就算是老化了,晋升到老年代) 这个阶段会发生stop the world的暂停,Eden和survivor的大小需要计算,为了进入下一次的年轻代收集,一些信息的统计也同时进行,为了帮助这个计算。设定的目标暂停时间也会纳入考虑的因素。 这个方法让改变这些区域大小变的容易,根据需要增大或者减少。 5、年轻代GC之后 存活的对象已经被复制或者移动到survivor和老年代区域。 刚晋升的对象如深蓝色所表示,绿色的表示survivor区域 总结一下,关于年轻代的GC有如下几点: 一整块堆内存被分成多个小区域 年轻代是由一些列的不连续的区域组成的,这有助于随时改变它的大小 年轻代的垃圾收集,或者称年轻代GC,是stop-the-world的一个事件,所有的应用程序线程必须停止,等待这个操作完成。 年轻代的GC,又多个线程并行处理 存活的对象被复制或者移动到新的survivor区域或者老年代区域

2014年7月7日 · 1 分钟

[翻译]G1垃圾收集器(三) 之 G1垃圾收集器

G1 GC 垃圾优先收集器(G1 GC) 是一个服务器型的垃圾收集器,它的目标是大内存,多处理器的机器。高概率的满足一个垃圾回收暂停时间的目标下,实现大吞吐量。(译者注:例如设置的垃圾回收暂停时间是200ms,G1可以保证90%的垃圾回收暂停时间都在200ms以内,从而保证大的吞吐量),在jdk1.7update4 及其以后的版本完全支持G1 GC,G1 GC主要是为下面这样的应用设计的: 就像CMS收集器一样,可以和应用线程并行处理 紧凑的空间,不会因为GC导致长时间的暂停 GC的暂停时间,希望更多的是可以预见的(译者注:意思就是说,大部分GC时间都是可以控制的,因为G1可以保证90%的GC都在目标时间以内) 不想牺牲大吞吐量的性能 不需要更大的内存 G1 GC的长期计划,就是为了替代CMS GC。比较CMS GC和G1,他们很多不同点表明G1 GC是个更好的解决方案。其中一个不同点就是G1是一种压缩收集器,G1对堆栈的充分压缩,可以避免使用细粒度的未分配内存列表去分配内存,而是使用region的概念。这大大简化了收集器的部分,更大程度上消除了潜在的碎片问题。此外,G1比CMS提供了更多的对收集暂停时间的预测,允许用户设定暂停时间期望目标。 G1操作概述 老的垃圾收集器(像serial,parallel,CMS),全部把堆栈划分成三个区域:年轻代,老年代,以及固定大小的永久代。 所有的对象,最终在三个区域中的一个中消亡。 G1 GC采用不同的方式: 堆被分成一组大小相等的区域,每一块区域对应虚拟机的一片连续内存。他们就像那些老的GC一样,也分成三组(Eden,Survivor,Old)。但是这三种区域没有固定的大小,这在内存使用上提供了更大的灵活性。 G1 GC的执行过程,和CMS有很大类似,G1首先并行进行一个全局的标记,来确认堆内的所有对象的活跃度。在经过标记阶段之后,G1 GC就知道哪些区域大部分都是闲置的。G1 GC首先收集那些可以产生大片空白内存的区域,这就是为什么G1 GC被称为垃圾优先。就像名字一样,G1 GC聚焦它的收集和压缩能力在那些就要被可回收对象(也就是垃圾占满的区域。G1有一个可预测的时间暂停模型,可以让用户指定一个目标,这个模型基于实现这个目标来选择每次收集的区域的个数。 那些有G1确认已到回收时机的区域使用撤离的方式进行回收垃圾。G1从一个或者多个区域中复制对象集中到一个区域中,在复制的过程中进行压缩和释放内存。这个撤离的过程在多处理器中是必行处理的,目的就是减少暂停时间和增大吞吐量。因此每次垃圾收集,G1 GC都要在用户设定的暂停时间内不断的工作,以减少碎片的产生。CMS收集器不会做压缩工作,老的Parallel收集器之做全堆的压缩,这会导致相当大的暂停时间。 有一点要特别注意,G1 GC不是一个实时的收集器,它只是说更大可能的满足你设定的暂停时间,但是也不是一定的。通过前面的收集,G1 GC会大体估算出来在你指定的目标时间内能够收集多少区域。因此G1 GC有一个对收集成本的考量模型,G1 GC通过这个模型来决定哪些,多少区域需要收集来保证这个暂停时间目标。 注意:G1 GC有并发和并行的两种阶段,例如refinement, 标记, 清除阶段,是并发执行,stop-the-word是并行处理的(译者注:并发和并行的概念要研究下)。Full GC仍然是单线程处理的,但是如果你调优的恰当,这种情况是可以避免的。 G1 自占内存 如果你是从老的Parallel GC或者CMS GC转到G1,你会发现更的进程占用,这主要和统计类的数据结构有关,例如Remembered Sets和Collection Sets。 Remembered Sets简称RSets,跟踪一个区域内对象的引用,在堆内,每一块区域对应一个RSet。RSets能让每个区域的收集都是独立并行的。RSets整体占小于5%的内存。 Collection Sets 简称CSets,是GC中需要收集的区域的集合,在一个GC过程中,CSets中所有的活动的数据都要被挪动(包括移动和复制),CSets可以是Eden, survivor, old任意类型的区域,Csets对jvm的大小(译者注:只要是说内存吧)有小于1%的影响。 推荐使用G1 GC的情形 G1第一个关注点是,为那些需要大内存,低GC时延的应用提供一个解决方案。也就是说内存可以到6G甚至更大,可以保证在0.5秒以下稳定的暂停时间。 使用CMS GC和老Parallel GC的应用程序,在有以下一个或者多个特征的情况,比较适合切换到G1 GC: ...

2014年7月6日 · 1 分钟

[翻译]G1垃圾收集器(四) 之 回顾GC以及CMS

回顾分代GC和CMS、 并发标记扫描收集器(CMS),也被称作低暂停并发收集器。它是回收老年代。它试图利用和应用线程并发的收集尽可能多的垃圾,以减少因为垃圾收集导致的停顿。通常这种低延迟并发收集器不会对活动的对象进行压缩处理,也就是说它只做一个不对对象进行移动的垃圾回收,那么配分一个更大的内存的时候,碎片就会成为一个问题。 注意:CMS收集器在年轻代中使用和parallel 收集器相同的算法。 CMS收集阶段 CMS收集器在堆的老年代的回收上有以下几个阶段: 阶段 描述 初始化标记 (stop-the-world事件) 标记老年代中依然存活的对象,包括那些从年轻代晋升来的,这个过程通常比较短暂(译者注:存活我理解为被程序引用,不可回收的对象) 并发标记 和应用程序并发执行的,扫描整个老年代,是从那些标记的对象,或者间接标记的对象开始扫描。第2、3、5个阶段是并发执行的,在这个过程中,在CMS的代中新分配的对象(包括晋升的对象)会被立即标记为存活状态。 重新标记 (stop-the-world事件) 该阶段发现那些被并发标记错过的对象,因为并发标记是和应用程序并发执行的,在标记线程完成对某个对象的跟踪那刻,应用程序可能对对象进行了更新。 并发清理 收集那些在标记阶段没有标记的对象,消亡对象所占的空间会被添加到释放列表里用于重新分配,对死亡对象的聚集工作就发生在这个点。注意:存活的对象不会被移动。 并发重置 做一些数据结构的清理工作,为下一次收集做准备 回顾垃圾收集的步骤 接下来,让我们回顾一下CMS GC的操作步骤: 1、CMS GC的堆结构 整个堆被分成三个部分 年轻代被分割成一个Eden和和两个survivor区域,老年代是延续空间,对象的收集就在这片区域,没有压缩操作直到一个full GC发生。(译者注:读到这里,我们应该很清楚压缩的意思就是说不仅仅对垃圾对象进行收集,还对存活的对象进行腾挪,聚集在一起,腾出更多的空间,不要让对象散落在不同的空间,这就是内存碎片,内存碎片导致无法分配一块连续的大内存) 2、在CMS中,年轻代的GC是怎么工作的? 年轻代被标记为绿色,老年代为蓝色,你的JAVA程序在运行一段时间后,可能会像下面这个样子.对象分散的分布在老年代中: 使用CMS,老年代是对象消亡的地方,它们不会被移动,也不会被压缩,除非遇到一个Full GC. 3、年轻代的收集 存活的对象从 Eden 区域或者一个 survivor区域,移动到另外一个survivor区域,一些老的对象如果达到了晋升阈值,就会被提升到老年代中。 4、年轻代GC之后 年轻代的收集之后,Eden区域和其中一个survivor区域会被清理 新晋升的对象是深蓝色区域,绿色区域年轻代中存活下来,还没有晋升到老年代的对象。 5、CMS中老年代的收集 两次stop_the_world的时间是初始化标记和重新标记阶段,当老年代达到一定的占有率,CMS才开始登场了。 (1)初始化标记是对存活的对象进行标记,只有短暂的停顿(2) 并发标记阶段,是和应用程序一起执行的,也害死标记存活的对象。最终在第(3)阶段去标记那些上一阶段(2)遗漏的对象。 6、老年代的收集-并发清理 在上一阶段没有被标记的对象会在这里消亡,这里没有压缩。 ...

2014年7月6日 · 1 分钟

[翻译]G1垃圾收集器(二) 之 java技术和JVM

java概述 java是一门编程语言和计算平台,第一个版本有Sun 微系统公司在1995年发布,它是支撑java程序,包括公用事业,游戏,和商业等应用的底层技术。java程序跑在全球超过850万的个人电脑,以及数十亿的设备中,包括移动电话和电视设备。java是有一些关键组建的集合,作为一个整体,构建了所谓的java平台 java运行时版本(JRE) java是一门面向对象的编程语言,包括以下几方面特性: 平台无关性,java应用程序,被编译成字节码,存储在类文件中,并在jvm中加载执行。既然java是运行在jvm中,那么它可以在任意不同的操作系统和设备中运行。 面向对象,java作为一门面向对象的语言,获取了C/C++的一些语言特性,并加以改进。 自动垃圾回收机制,Java自动分配和释放内存,所以程序不用承担这份任务。 丰富的标准库,java预置了大量的对象,用于实现各种功能,包括关于输入/输出,网络,日期等方面的操作。 java开发工具包(JDK) JDK是开发java程序的一些工具的集合。使用JDK,你可以编译你用java写的程序,并在JVM中运行它,此外JDK还提供打包、发布你的应用的工具。 JDK和JRE有一样的java应用程序接口(API),JAVA API是提供给开发者开发应用,预先打包的一些类库的集合,这些JAVA API让开发者在开发一些常用功能的时候变的很容易,包括字符串的操作,日期和时间的处理,网络相关。还实现了一些数据结构(例如:链表,map,栈,队列等) java虚拟机(JVM) java虚拟机是一个抽象的计算机,JVM是一个程序,就像一个虚拟的机器,可以让写的程序在上面执行,这样每一个java程序,都是面向同一份接口和类库。针对每一种操作系统,java虚拟机都可以把java程序翻译成操作系统对应的指令,所以java程序是不依赖平台的。 jvm的第一份原型是有sun公司开发的,在一个在手持设备上类似个人数字助理软件中,实现了jvm的指令集。目前oracle公司虽然在手机、桌面或者服务器设备上实现了jvm。但是jvm并不限定特殊的实现技术,主机硬件和主机操作系统。可能这个解释不是很确切,但是的确对于一个硅晶体管CPU,通过编译它的指令集来就可以实现(JVM),或者通过微码,甚至直接通过硅晶体管来实现,也不是不可能实现的。 jvm对于java语言写的程序是不认识的,它只认一种特殊的二进制格式-class文件。一个class文件包含jvm的指令集(或者说字节码)和一个符号表,以及一些其它辅助信息。 为了安全起见,jvm对class文件中的代码,有严格的语法和格式限制。然而任何编程语言,如果可以编译成class文件,都可以在jvm中运行。所以作为一个一般可用,与平台无关的平台,对于其它语言的实现,也可以把jvm作为他们的运行载体。 探索JVM结构体系 Hotspot体系 HotSpot JVM有一个支持强大特性和功能,可实现高性能和高扩展性的结构体系,例如HotSpot JVM的JIT编译器的动态优化功能。换句话说,它可以在java应用运行时做一些优化,让它生成对于底层系统体系更高效的本地机器指令。此外,通过对运行环境、多线程垃圾回收机制的不断成熟演进,HotSpot JVM甚至可以扩展到更多的计算机系统中。 JVM的主要组成部分,包括:类加载器,运行时数据区,和执行引擎。 关键 Hotspot组件 在JVM中,涉及到性能的关键部件如下图高亮显示。 在性能调优方面主要聚焦三个组件:堆栈是用于存储对象数据的。这块区域在程序启动之后就有GC管理了,大多数调优选项是和调整堆栈大小以及选择合适的GC有关的。JIT编译器对性能也有影响,但是对于新版本的JVM很少需要你手动调优。 基本性能 通常情况下,调优一个Java应用程序时,重点是主要是两个目标中的一个:响应速度和吞吐量。我们会请参考这些概念的教程的进展。在教程讲解的过程中,我们会涉及到下面这样概念: 响应速度 响应速度是指在接收到请求数据之后,一个应用程序或者系统响应的有多快。例如包括: 一个桌面UI对一个事件的响应有多快 一个web站点多久返回一个页面 一个数据库查询多长时间能返回 对于响应速度要求很高的应用程序,大的时间暂停是不被接受的,重点就是在短时间内响应。 吞吐量 吞吐量就是一个应用在一定的时间内做尽量多的事情,衡量吞吐量包括以下的例子: 在给定时间内完成事物的多少 一个批处理程序在1个小时内完成的任务数 数据库在一个小时内可做的查询数 对大吞吐量要求高的程序,高暂停时间是可以接受的,由于大吞吐量的程序是更关注于在一定较长时间内,所以快速的响应时间不是它要考虑的。

2014年7月4日 · 1 分钟

[翻译]G1垃圾收集器(一) 之 概论

目的: 本教程包含关于G1 垃圾收集器(简称 G1 GC)使用的基本知识,以及如何在Hotspot JVM中使用,你可以学习到G1 GC的内部是如何工作的,一些关键的命令行参数,以及日志选项。 完成时间: 1个小时左右 简介: 本培训教程涵盖了java中关于java虚拟机(JVM) G1 GC的基本知识,在教程开始总体介绍了一下提供了G1 GC的JVM,以及其性能,然后会介绍CMS GC在JVM中如何工作的,一步步引导出G1 GC在JVM中又是如何工作的。随后一个章节介绍G1 GC的所有命令行选项。最后,你会了解到使用G1 GC时用到的日志选项。 软硬件环境需求 下面是列出的对硬件和软件的需求: 一台装有window XP以后版本、Mac OS X 或者linux的机器。注意window7以及着手测试了,但是不包括所有的平台。但是在Mac OS X 或者linux平台上一切运行正常,最好机器是多核心的。 java 7 update 9 或者以后的版本 最新的Java 7 Demos and Samples Zip file 先决条件 在开始本教程之前,你需要: 如果你没有下载和安装java 7 update 9 或者以后的版本,请到这里下载:http://www.oracle.com/technetwork/java/javase/downloads/index.html 下载安装 Demos 和 Samples zip 从上面同样的地址 解压到一个目录中,例如: C:\javademos

2014年7月3日 · 1 分钟

java多线程学习(八) 之 线程池

线程池的目的是为了使用有限的线程处理大并发的任务。jdk自带丰富的线程池适应不同的应用场景。一般线程池需要考虑这样四个问题: 问题一:Core and maximum pool sizes: 线程池的大小,线程池的目的就是使用为了节省资源,使用有限的线程处理大并发的任务。你要为的线程设置最大的线程数maximumPoolSize,表示该线程数最大创建这么多线程,当要处理的任务多于线程数的时候,就放到队列里去等待,当有线程完成运行任务的时候,再从队列里取出来执行。 maximumPoolSize是最大的线程数,是最坏的打算,如果你认为n个线程就可以处理得了你的任务,你可以设置corePoolSize。表示线程池只创建corePoolSize个线程在处理任务,当任务已经把缓存队列占满,corePoolSize个线程处理不过来的时候,才会去创建新的线程。 问题二:keep-live 如果任务少,线程长时间闲置,就会浪费资源,但是一旦关闭了这个线程,等任务来了再创建线程又浪费时间。你可以根据你的任务的并发数,为线程池设置keep-live时间,当多长时间没有任务处理的线程自动关闭。这样当任务数少的时候,池子里的线程数就会相应减少,当任务多的时候,线程数也会随之增加。 问题三:queue 线程池一般都要指定一个缓存队列,因为你不能保证线程池里的线程足够处理你的任务,当并发任务数超过这个线程的核心线程数的时候,就会把任务暂且存放在这个缓存队列里。 问题四:Reject Policy 当线程池里的maximumPoolSize的线程都在处理任务,queue里的任务也占满了。也就是系统达到了满负荷,再有新任务进来,这时候应该怎么处理,这需要一个Policy 处理策略,对这个新任务应该怎么处理 需要指定一个策略RejectedExecutionHandler java.util.concurrent.ThreadPoolExecutor.AbortPolicy 任务填不进去,直接报错 java.util.concurrent.ThreadPoolExecutor.DiscardPolicy 直接丢弃 java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy 丢一个队列中最老的任务,把新任务加进去 如果你没有其他方面的要求,一个线程池基本只要考虑上面四个方面。 具体线程池的创建和使用可以简单参考: http://zhaoyanblog.com/archives/203.html

2014年6月12日 · 1 分钟

java多线程学习(七) 之 atomic操作

有些时候,我们希望对一个数值类型的操作是一个原子操作,比如一个计数器,我们希望对一个计数器++i 之后,可以拿到的i是++之后的值。 你如果是这样写 m=++i; 当前i可能是2,再执行++i之后是3,此时如果另外的线程也进行了++操作,那当赋值给m的时候,可能有一定的几率就不是3了。 要保证这样的原子操作,可以使用线程同步的方式来实现,但是使用AtomicInteger,会更方便一些。 例: AtomicInteger atomic = new AtomicInteger(0); int num = atomic.incrementAndGet(); incrementAndGet方法保证返回的结果是+1之后的结果。 此外它还有一系列的原子性操作: atomic.getAndDecrement(); //num = i--; atomic.getAndIncrement(); //num = i++; atomic.getAndAdd(3); //num = i; i+=3; atomic.getAndSet(3); //num = i; i=3; atomic.decrementAndGet(); //num = --i; atomic.incrementAndGet(); //num = ++i; atomic.addAndGet(3); //i+=3; num = i; //atomic.set(3); //i=3; atomic.compareAndSet(2, 3); //if(i==2) i=3; 这些操作,保证了对一个数值对象,进行基本操作的原子性。保证了一个线程在对一个数值进行操作之后,拿到得是自己操作后的结果,而不是别的线程操作后的结果。 在实际中使用到的场景主要是多线程下的计数器,版本号等,虽然和Lock实现(ReentrantLock)的原理都一样,都是使用了UnSafe类native的compareAndSwapInt等方法实现的,但是至少在用法上更简洁,更方便。 除了AtomicInteger之外,还有AtomicBoolean,AtomicLong。都是保证对数值类型的操作的原子性。 AtomicReference 是对一个对象引用的原子操作。 举例: MyObject p1 = new MyObject(1); AtomicReference<MyObject> pR = new AtomicReference<MyObject>(p1); pR.compareAndSet(p1, new MyObject(2)); 相当于: MyObject p1 = new MyObject(1); MyObject pR = p1; if(pR == p1) { pR = new MyObject(2); } 保证对一个对象引用的原子操作。 此外还有他们的衍生类型 AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray 保证对数组类型里的元素的操作的原子性。 AtomicIntegerFieldUpdater/AtomicLongFieldUpdater/AtomicReferenceFieldUpdater 保证对一个对象的成员的操作的原子性

2014年6月7日 · 1 分钟

java多线程学习(六) 之 Condition

和synchronized一样,Lock是为了线程互斥而设计的,在synchronized中,使用对象隐式锁的wait notify(notifyAll)来实现线程之间的通信,同样的功能在Lock中是通过Codition来实现的。Condition除了完成wait和notify的基本功能之外,它的好处在于一个Lock可以通过创建多个Condition,选择性的去通知wait的线程。 官方解释: Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations. Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods. Condition把隐式锁的wait notify notifyAll功能提取出来,赋予确切的对象,让一个对象有个多等待集(wait-sets),这些condition也是和Lock实例绑定的,换句话说,Lock对象代替了synchronized,condition代替了wait、notify(notifyAll)等方法。 因为一个Lock可以生成多个Condition,所以condition可以让所有的因Lock等待的线程分成几个相互等待的子集合,也就是前面提到的wait-sets. Conditions (also known as condition queues or condition variables) provide a means for one thread to suspend execution (to “wait”) until notified by another thread that some state condition may now be true. Because access to this shared state information occurs in different threads, it must be protected, so a lock of some form is associated with the condition. The key property that waiting for a condition provides is that it atomically releases the associated lock and suspends the current thread, just like Object.wait. ...

2014年6月2日 · 2 分钟

java多线程学习(五) 之 Lock

java里的Lock提供了比synchronized更灵活的线程同步操作,可以让你更自由的获取锁,释放锁。 官方解释: Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements. They allow more flexible structuring, may have quite different properties, and may support multiple associated Condition objects. Lock的实现类提供了比synchronized更广泛意义上锁操作,它允许用户更灵活的代码结构,更多的不同特效,支持多个相关的Condition 对象。 这里我们先不去了解什么是Condition对象,我们主要还是看Lock的基本操作,来完成synchroized可以完成的工作。 A lock is a tool for controlling access to a shared resource by multiple threads. Commonly, a lock provides exclusive access to a shared resource: only one thread at a time can acquire the lock and all access to the shared resource requires that the lock be acquired first. However, some locks may allow concurrent access to a shared resource, such as the read lock of a ReadWriteLock. Lock 是一个对多线程访问共享资源进行控制的工具,通常 一个锁提供了一个共享资源的独占访问, 也就是说在同一时间,只有一个线程可以获取到该锁,在获取锁之后,才能对共享资源进行访问。当然也有的锁允许并发访问资源,例如这种特殊的锁ReadWriteLock ...

2014年6月1日 · 2 分钟

java多线程学习(四) 之 wait和notify

ynchronized让线程之间互斥,但是有时候因为业务逻辑,需要主动释放锁,让其它的线程执行,这就需要线程之间通信,我们知道JAVA里每个对象都有个隐式锁,JAVA为每个对象都提供了wait和notify(还有notifyAll)方法,分别用于本线程主动释放锁,进入等待状态,和唤醒其它进入等待的线程。 官方解释: Causes the current thread to wait until another thread invokes the java.lang.Object.notify() method or the java.lang.Object.notifyAll() method for this object. wait会导致当前线程进入等待,直到其它线程执行该对象的notify()或者notifyAll方法 The current thread must own this object’s monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object’s monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution. 首先当前线程在调用wait之前,必须先获取到该对象的monitor就是所谓的隐式锁,也就是synchronized的作用。这个线程就会释放对隐式锁,并且进入等待,直到另外一个获得锁的线程,通过notify和notifyAll唤醒它。它在被唤醒后,并不会继续往下执行,而是需要再次去获取锁,获取锁之后,继续执行。 ...

2014年5月29日 · 1 分钟