JVM笔记之GC

1. JVM 运行时数据区域: 1)程序计数器(线程私有),当前线程所执行字节代码的行号指示器,如果执行的是java方法,则值为字节码的指令地址,如果是native方法,则为空。2)Java虚拟机栈(线程私有),存储局部变量,操作数栈,动态链接,方法出口等。其大小在编译时确定。3)本地方法栈,和虚拟机栈类似,只是执行本地方法。4)Java 堆,垃圾收集的主要区域,最大的一块内存。5) 方法区(线程共享的区域),存储类信息,常量,静态变量,即时编译后的代码,有些人称之为永久代,然而并不相同。6) 运行时常量池,方法区的一部分,存放编译器生成的字面量和符号引用。7)直接内存,不受堆的限制,但受到物理内存的限制。计算内存限制时容易忽略直接内存。

2. GC Root 对象:1)虚拟机栈(栈帧本地变量表)中引用的对象; 2)方法区中类静态属性引用的对象; 3)方法区中常量引用的对象; 4)本地方法栈中JNI引用的对象。

3. 引用的类型:1) 强引用:正常的,还在被使用的引用。GC永远不会回收。2)软引用:还有用,但并非必须的对象,在快要发生OOM时,GC会先尝试回收他们。3)弱引用:只能生存到下次GC之前。4)虚引用:唯一目的是GC时,系统能得到一个通知。

4. 即使在可达性分析中不可达的对象,也并非非死不可。一个对象真正死亡,至少要经过两次标记。如果对象不可达,将会被第一次筛选,筛选的条件是此对象有没有必要执行finalize方法。当对象没有覆盖finalize方法,或者已经被调用过,虚拟机认为没有必要执行。如果有必要执行finalize方法,这个对象会被放入F-Queue,稍后有一个虚拟机建立的,低优先级的Finalizer去执行finalize,但是并不承诺他会执行完成。这样做是为了避免由于对象的finalize执行缓慢或者陷入死循环二阻塞其他对象的回收。finalize只是提供了一个方法来拯救自己。但是这种方法并不推荐。

5. 回收方法区。回收废弃的常量和无用的类。性价比比较低。无用的类的判定:1)该类所有实例已经被回收;2)加载该类的classLoader已经被回收;3)该类对应的Class对象没有被引用。

6. GC 算法

1)标记清除:标记和清除,容易产生内存碎片。2)标记整理:类似于标记清除,但是标记完后将存活对象向一端移动,适用于老年代。3)复制算法:划分成1:1的两块,一块用完,将存活对象考到另外一块。4)分代收集,新生代每次都有大量对象死去,只有少量存活,可以用复制算法;老年代存活率高,没有额外空间进行担保,可以使用标记清除或者标记整理算法。

7.安全点和安全区域

8. 垃圾收集器(收集算法的实现)

JVM笔记之GC

1) Serial 最基本、发展历史最悠久的收集器,曾经在新生代的唯一选择。单线程收集器,只会使用一个CPU去完成垃圾收集工作,在垃圾收集时必须暂停其他所有工作线程知道手机结束。在用户不可见的情况先把用户正常工作线程停掉。这是虚拟机运行在Client模式下的默认新生代收集器。它简单而高效,对于单个CPU环境来说,Serial收集器没有线程交互的开销。在Client应用中,分配给虚拟机的内存一般不大,收集几十兆到一两百兆的内存,停顿时间可以在几十到一百多毫秒,只要不频繁,都可以接受。

2) ParNew Serial收集器的多线程版本。除了多线程垃圾收集的参数之外,其余行为包括Serial可用的控制参数,收集算法,Stop The World,对象分配规则、回收策略等与Serial完全一样。它是许多运行在Server模式下虚拟机首选的新生代收集器。除了Serial之外,只有ParNew能与CMS配合工作。

3) Parallel Scavenge 其他收集器关注点是尽可能缩短垃圾收集时用户线程停顿时间,而Parallel Scavenge则关注吞吐量。吞吐量=用户代码运行时间/(用户代码运行时间+垃圾收集时间)。高吞吐量适合在后台运算而不需要太多交互的任务。

4) Serial Old Serial收集器的老年代版本,单线程收集器,使用标记整理算法,主要意义在于给Client模式下的虚拟机使用。在Server模式下,搭配 Parallel Scavenge使用。另外是最为CMS的后备方案,在并发收集Concurrent Mode Failure时使用。

5) Parellel Old 它是Parallel Scavenge的老年代版本,使用多线程和标记整理算法。由于它的出现,吞吐量优先有了名副其实的应用组合。

6) CMS 一种以获取最短回收停顿时间为目标的收集器,目前很大一部分Java引用集中在互联网站或者B/S系统的服务端上。CMS基于标记清除,分为四个步骤:

初始标记;并发标记;重新标记;并发清除。其中初始标记和重新标记需要STW,初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。并发标记就是进行GC Roots Tracing的过程,重新标记则是为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般比初始标记稍长,但是远比并发标记段短。整个过程中耗时最长的并发标记和并发清除过程收集器线程和用户线程一起工作,总体来说,CMS的内存回收过程与用户线程是并发的。

CMS对CPU资源很敏感。其实面向并发设计的程序对CPU资源都敏感。在并发阶段,虽然不会导致应用程序变慢,但是总吞吐量会降低(因为一部分CPU或者说用户线程被拿来用作垃圾回收了)。

CMS默认垃圾收集线程数是(CPU数量+3)/4。CMS无法处理浮动垃圾,可能出现Concurrent Mode Failure失败而导致另一次Full GC。由于CMS运行时,伴随程序运行会产生新的垃圾。这些垃圾出现在标记之后,CMS只能在下次GC时在清理掉。因此CMS收集器不能像其他收集器那样等到老年代几乎完全填满在进行收集,需要预留一部分空间供程序运行使用,使用-XX:CMSInitiatingOccupancyFraction 控制。如果在CMS运行期间预留的内存无法满足程序需要,就会出现Concurrent Mode Failure,需要临时使用Serial Old来重新对老年代收集。如果-XX:CMSInitiatingOccupancyFraction 太高,更容易产生Concurrent Mode Failure,性能反而降低。如果太低,则会导致CMS更频繁。

CMS基于标记清除,容易产生内存碎片。

JVM笔记之GC

7) G1 收集器 

G1能充分利用多CPU、多核环境的硬件优势来缩短STW的时间。

G1也是采用分代收集。

G1采用标记整理,没有内存碎片的问题。

G1可以设置最长停顿时间,具有可预测的停顿。

使用G1时,Java堆的内存布局与其他收集器有很大不同。虽然有新生代和老年代的区别,但是新生代和老年代不再是物理隔离的,而是被划分成了多个大小相等的独立区域。G1之所以能够预测停顿时间,是因为可以避免在整个Java堆上进行全区域垃圾收集。G1跟踪各个Region里面垃圾堆积的价值大小维护一个有限列表,优先回收价值最大的区域。

主要步骤:初始标记;并发标记;最终标记;筛选回收

http://www.oracle.com/technetwork/tutorials/tutorials-1876574.html

9. JVM常用指令,使用这些命令一定要注意权限问题,该命令执行用户必须和jvm进程用户一致,否则会出现一些奇怪的看似不相关的提示。

jps -lvm

jstat -class <pid>

jstat -gc <pid>

jinfo:可以动态调整参数而无需重启进程,例如打开或者关闭GC日志

 jinfo -flag +PrintGCDetails <pid> 打开GC日志

 jinfo -flag -PrintGCDetails <pid> 关闭GC日志

jinfo -flag MaxTenuringThreshold <pid> 查看MaxTenuringThreshold的但前值,即使没有指定

jinfo -flag < name >=< value > <pid>

 java -XX:+PrintFlagsInitial XX选项的默认值

 java -XX:+PrintFlagsFinal XX选项运行时生效值,该选项用于启动一个jvm时。其中的:=表示override了PrintFlagsInitial 的默认值。如果不指定其他参数,默认是运行java,而java的输出是输出java的help信息。可以通过运行 java -XX:+PrintFlagsFinal -version 做一个简单的测试。

 jmap -dump:format=b,file=dump.bin <pid>

jmap -heap <pid>

jhat <file> 对jmap dump出来的文件进行解析,以服务器的形式显示这些信息

jconsole和jvisualvm

jstack <pid> 可以调试死锁和占用时间长的线程