随着时间的推移,我觉得有必要将一些之前相对模糊但是对自身技术提高会有帮助的原理、概念、实现进行一下系统的整理,所以就从JVM系列开始吧。

本系列主要参考《Java虚拟机规范(第二版)》、周志明先生写的《深入理解Java虚拟机》,具体虚拟机主要参照Hotspot。

一.Runtime Data Areas

在运营及维护我们的站点或者应用时,需要针对crash或者内存溢出、内存泄露定位问题以及调优等工作,而Java这门语言或者是平台提供了一个可以将内存分配、回收屏蔽掉的JVM,所以我们非常有必要了解一下JVM的运行时数据分区情况。

154905403.png

图1-Runtime Data Areas

从上图可知,我们每创建一个Tread,JVM都会为该线程分配一个Program Counter Register、一个Java Virtual Machine Stack、一个Native Method Stack,以上三个区域都是线程私有,而Heap与Method Area是线程共享。

我们分别了解一下每个区域的作用、存储的数据、线程私有\共享情况、可能出现的错误或异常。

1.Program Counter Register(程序计数器)

a.作用:被字节码解释器用来选取下一条需要执行的字节码指令。我们需要明确一点,Java虚拟机的多线程机制是通过线程轮流切换并分配处理器执行时间的方式实现的,即在某一个特定的时刻,一个处理器只会执行一条线程中的指令,从这个层面,该区域需要线程私有。

b.存储的数据:以当前线程正在被执行的是Java方法还是Native方法。

(1).Java方法:本线程要被执行的下一条字节码指令地址。

(2).Native方法:空(Undefined)。

c.线程私有/共享情况:线程私有,每一个线程都持有一个程序计数器。

d.错误或异常:虚拟机规范中未规定该区域会抛出任何异常或错误。

2.Java Virtual Machine Stack(虚拟机栈)

a.作用:从名字上就可以猜测,这是一个Stack。该区域描述的是Java方法执行的内存模型:

每个Java方法执行被执行时都会常见一个栈帧(Stack Frame),该Stack Frame存储了方法的局部变量表、操作栈、动态链接、方法出口等信息。每一个Java方法从被调用到执行完成都对应着一个Stack Frame从进Stack到出Stack的过程。

b.存储的数据:本区域就是一个Stack结构,存储的就是一个个被执行的Stack Frame.

c.线程私有/共享情况:线程私有。

d.错误或异常:两种异常(更精确的说是Error)

(1).StackOverflowError:请求深度超过JVM允许的深度时抛出,简单点理解就是这个Stack满了而已。

(2).OutOfMemoryError:无法申请到足够的内存时抛出。

4.Native Method Stack(本地方法栈)

这个区域的结构及作用与虚拟机栈类似,只是这个区域描述的是Native方法执行的内存模型,所以HotSpot干脆把Java Virtual Machine Stack与Native Method Stack合并了。这个区域也是线程私有的,也会抛出StackOverflowError与OutOfMemoryError.


5.Java Heap(Java堆)

a.作用:存放对象实例(包括对象实例与数组)。

b.存储的数据:对象实例。

c.线程私有/共享情况:线程共享。

d.错误或异常:OutOfMemoryError.

该区域是垃圾收集器管理的主要区域,所以为了更快的分配内存、更好的进行垃圾回收,Java Heap还可以进行细分:新生代与老年代。如果在细分的话有新生代又由Eden空间、From Survivor空间、To Survivor空间组成。

6.Method Area(方法区)

a.作用:存储被虚拟机加载的类信息、常量、静态变量、JIT编译后的代码。

b.存储的数据:同上

c.线程私有/共享情况:线程共享

d.错误或异常:OutOfMemoryError.



7.Runtime Constant Pool(运行时常量池)

a.作用:存储编译期生成的各种字面量和符号引用

b.存储的数据:同上

c.线程私有/共享情况:线程共享

d.错误或异常:OutOfMemoryError.


该区域是Method Area的一部分。


二.Some Options

JVM Options:

参数名
含义默认值说明
-XmsJava Heap初始大小物理内存的1/64,但是小于1GB
-XmxJava Heap最大值物理内存的1/4,但是小于1GB
-Xmn新生代大小
该区域由Eden空间、From Survivor空间、To Survivor空间组成
PermSize方法区初始大小物理内存的1/64
MaxPermSize方法区的最大值物理内存的1/4
-XssJVM Stack大小

JDK5.0之前默认256K,之后默认1M.

在物理内存不变的情况下,可以通过减小该值来获取更大的线程创建数量。

NewRatio新生代与老年代的比值
如果-XX:NewRatio=4表示新生代与老年代的比值为1/4
SurvivorRatioEden空间与Survivor空间的比值

LargePageSizeInBytes内存页的大小
用法-XX:LargePageSizeInBytes=128m
UseFastAccessorMethods原始类型的快速优化

DisableExplicitGC
忽略来自System.gc()方法触发的垃圾收集
默认关闭




Help Options

-XX:+PrintGC

输出形式:

[GC 118250K->113543K(130112K), 0.0094143 secs]
[Full GC 121376K->10414K(130112K), 0.0650971 secs]

-XX:+PrintGCDetails

输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]
[GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]

-XX:+PrintGCTimeStamps


-XX:+PrintGC:PrintGCTimeStamps

可与-XX:+PrintGC -XX:+PrintGCDetails混合使用
输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]
-XX:+PrintGCApplicationStoppedTime打印垃圾回收期间程序暂停的时间.可与上面混合使用
输出形式:Total time for which application threads were stopped: 0.0468229 seconds
-XX:+PrintGCApplicationConcurrentTime打印每次垃圾回收前,程序未中断的执行时间.可与上面混合使用
输出形式:Application time: 0.5291524 seconds
-XX:+PrintHeapAtGC打印GC前后的详细堆栈信息

-Xloggc:filename把相关日志信息记录到文件以便分析.
与上面几个配合使用


-XX:+PrintClassHistogram

garbage collects before printing the histogram.

-XX:+PrintTLAB查看TLAB空间的使用情况

XX:+PrintTenuringDistribution查看每次minor GC后新的存活周期的阈值

Desired survivor size 1048576 bytes, new threshold 7 (max 15)
new threshold 7即标识新的存活周期的阈值为7。