重点!!!!这并不是全面的JVM整理,仅仅只是记录一下白鹤翔的课
-XX 系统级别的jvm配置
+代表启用 -代表禁用
默认初始化堆内存64M 最大堆内存1G
基本策略: 尽可能将对象预留在新生代中,减少老年代GC的次数。
-XX:+PrintGC 使用这个参数,虚拟机启动后,遇到GC就会打印日志
-XX:+UseSerialGC 使用串行回收器
-XX:+PrintGCDetails 可以查看详细信息,包括各个区的情况
-XX:+PrintCommandLineFlags 可以将传给虚拟机的参数输出出来
-Xms: 设置java程序启动时初始堆大小
-Xmx: 设置java程序能获得的最大堆大小
-Xmx20m -Xms5m
-Xmn: 可以设置新生代的大小,设置一个比较大的新生代会减少老年代的大小,
这个参数对系统性能以及GC行为有很大的影响,新生代大小一般会设置整个堆空间的1/3到1/4左右。
-XX:NewRatio 老年代/新生代
-XX:SurvivorRatio 设置新生代中eden和s0或s1的比例。默认8:1
OOM(Out of Memory) 内存溢出
Java程序运行过程中,如果堆空间不足,则会抛出内存溢出的错误。一旦这类问题发生在生产环境,可能引起严重的业务中断,Java虚拟机提供了-XX:+HeapDumpOnOutOfMemoryError
,使用该参数可以在内存溢出时导出整个堆信息,与之配合使用的还有参数-XX:HeapDumpPath
,可以设置导出堆的存放路径。
内存分析工具: Memory Analyzer 1.5.0
地址: http://download.eclipse.org/mat/1.5/update-site/
具体用法可百度,白鹤翔P都没讲
Java虚拟机提供了参数-Xss
来指定线程的最大栈空间,整个参数也直接决定了函数可调用的最大深度。
-Xss5m -Xss10m 之类...
1.8以前是永久区,1.8以后是MetaSpace
和Java堆一样,方法区是一块所有线程共享的内存区域,它用于保存系统的类信息,方法区(永久区)可以保存多少信息,可以对这个进行配置。默认情况下,-XX:MaxPermSize
为64MB,如果系统运行时生产大量的类,就需要设置一个相对合适的方法区,以避免永久区内存溢出的问题
-XX:PermSize=64M -XX:MaxPermSize=64M
直接内存是Java程序中非常重要的组成部分,特别是广泛用在NIO中,直接内存跳过了Java堆,使Java程序可以直接访问原生堆空间,因此在一定程度上加快了内存空间的访问速度。但是说直接内存一定就可以提高内存访问速度也不见得,具体情况具体分析咯
相关配置参数: -XX:MaxDirectMemorySize
, 如果不设置默认值为最大堆空间,即-Xmx
。直接内存使用达到上限时,就会触发GC,如果不能有效的释放空间,也会引起系统的OOM。
目前Java虚拟机支持Client和Server两种运行模式。使用参数-client
可以指定使用Client模式,使用-server
即使用Server模式。可以直接在命令行查看当前计算机系统自动选择的运行模式。java -version
即可。
二者区别: Client模式相对Server模式启动较快,如果不追求系统的长时间使用性能。仅仅是测试,可以使用Client模式。而Server模式则启动比较慢,原因是会对其进行复杂的系统性能信息收集和使用更复杂的算法对程序进行优化。 一般我们的生产环境都会使用Server模式,长期运行其性能要远远快于Client模式。
JVM不错的博客:
http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html
垃圾回收器的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收器可以高效的执行,大部分情况下,会要求系统进入一个停顿的状态。停顿的目的是终止所有应用线程,只有这样系统才不会有新的垃圾产生,同时停顿保证了系统状态在某一瞬间的一致性,也有益于更好的标记垃圾对象。因此在垃圾回收时,都会产生应用程序的停顿。
讨论GC需要考虑几个问题
第一个问题,我们经历了程序计数法,根搜索算法。
在根搜索算法的基础上发展的标记-清除,复制,标记-整理主要解决后两个问题。
这三种算法各有其优缺点和适用范围,所以根据不同类型的对象使用不同类型的GC算法,由此产生了分代算法。
eden朝生夕死,s0 s1相当于复制算法的两半空间,同一时刻只有一块拥有对象。由于年轻代对象GC后存活率低,所以该代使用复制算法非常合适。tenure老年代由于对象几乎很少变化,使用标记-整理算法。
一般而言对象首次创建会被放置在新生代的eden区,如果有GC介入,则对象会离开eden区去往s0或者s1,如果没有GC介入,则对象不会离开eden区。 那么eden区的对象如何进入老年代呢? 一般来讲,只要对象的年龄达到(GC幸免的次数),就会自动离开年轻代进入老年代,对象年龄是由对象经历GC次数决定的,在新生代每次GC之后如果对象没有被回收则年龄+1。虚拟机提供了一个参数来控制新生代对象的最大年龄,当超过这个年龄范围就会晋升老年代。
-XX:MaxTenuringThreshold
默认情况下为15
总结: 根据设置MaxTenuringThreadhold参数,可以指定新生代对象经过多少次回收后进入老年代。
另外,大对象(新生代eden区无法装入时)会直接进入老年代。JVM里有个参数可以设置超过一定大小的对象直接进入老年代。
-XX:PretenureSizeThreshold
总结: 使用PretenureSizeThreshold可以进行指定进入老年代的对象大小,但是要注意TLAB区域优先分配空间。
TLAB全称是Thread Local Allocation Buffer即线程本地分配缓存,从名字上看是一个线程专用的内存分配区域,是为了加速对象分配而生的。每一个线程都会产生一个TLAB,该线程独享的工作区域,java虚拟机使用这种TLAB区来避免多线程冲突问题,提高了对象分配的效率。TLAB空间一般不会太大,当大对象无法在TLAB分配时,则会直接分配到堆上。
-XX:+UseTLAB 使用TLAB
-XX:+TLABSize 设置TLAB大小
-XX:TLABRefillWasteFraction 设置维护进入TLAB空间的单个对象大小,他是一个比例值,默认为64。即如果对象大于整个空间的1/64, 则在堆创建对象。
-XX:PrintTLAB 查看TLAB信息 必须禁用逃逸分析参数才能使用
-XX:ResizeTLAB 自调整TLABRefillWasteFraction阀值
一个对象创建在什么位置,我们的jvm会有一个比较详细的流程。根据数据的大小,参数的设置,决定如何创建分配以及其位置。
尝试栈上分配 --> 尝试TLAB分配 --> 是否满足进入老年代 --> eden分配 --> 结束
每一个箭头都有个失败与否的判断。
如果全部失败,就进入eden