JVM

2018-01-26  本文已影响0人  lwz9103

1.java运行时数据区

运行时数据区

1.1程序计数器(线程独有)

类似于计算机中的寄存器,记录当前线程执行的指令和行号。如我们所知,线程和进行都是cpu执行的时间片段,只是粒度大小不一样而已。所有在线程进行切换的过程中,需要保存线程进入等待时的指令行号,这样下一次才能找到从哪个位置继续执行。

1.2 虚拟机栈(线程独有)

当一个线程的栈深度大于虚拟机所允许的深度的时候,将会抛出StackOverflowError异常;如果当创建一个新的线程时无法申请到足够的内存,则会抛出OutOfMemeryError异常。

1.2.1栈帧结构

在执行方法时,会创建一个栈帧(一个方法对应一个栈帧),并压入虚拟机栈当中。


栈帧
1.2.2局部变量表和操作数栈
  javap -c Test.class > p.txt 反汇编字节码文件后,得到操作指令
操作数栈

上述方法执行的指令含义如下:

本地变量表: this, i, j    。顺序为0,1,2

1.加载本地变量表变量1的值,压入操作数栈中。 
2.加载本地变量表变量2的值,压入操作数栈中。
3.压入操作指令iadd,计算出结果并将之前的弹出栈外
4.返回int类型的值

更多汇编指令参考:http://bbs.gupaoedu.com/forum.php?mod=viewthread&tid=295&highlight=javap

1.2.3动态链接

多态情况下,方法动态绑定时,需要用到动态链接

1.2.4方法出口

当一个方法开始执行以后,只有两种方法可以退出当前方法:
当执行遇到返回指令,会将返回值传递给上层的方法调用者,这种退出的方式称为正常完成出口(Normal Method Invocation Completion),一般来说,调用者的PC计数器可以作为返回地址。
当执行遇到异常,并且当前方法体内没有得到处理,就会导致方法退出,此时是没有返回值的,称为异常完成出口(Abrupt Method Invocation Completion),返回地址要通过异常处理器表来确定。
当方法返回时,可能进行3个操作:
恢复上层方法的局部变量表和操作数栈
把返回值压入调用者调用者栈帧的操作数栈
调整 PC 计数器的值以指向方法调用指令后面的一条指令

1.3本地方法栈(线程独有)

调用本地方法时使用

1.4方法区

方法区

1.5heap

heap

2.java内存模型(JMM, java memory model)

2.1内存结构

内存模型
问题1:新生代的垃圾回收算法为什么用复制算法?
因为新生代的对象90%都是朝生夕死,用标记清除法容易造成内存碎片,而标记整理法代价太高
所以采用复制算法。
问题2:为什么新生代不直接设置两个区,比例为1:1,或者9:1
如果是1:1的,空间利用率只有50%。如果是9:1的话,大部分对象的分代年龄只能达到1,也就是经过一次minorGC就会进入到老年代中。
问题3:怎么控制survivor的比例和新老年代的内存比例?
-XX:SurvivorRatio=8
-XX:NewRatio=2
更多JVM参数请参考:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

2.2对象生命周期

2.2.1类加载检查

在对象创建之前,需要先判断类是否已经被加载,判断方式在当前类加载器中寻找该类的类信息,如果没有找到,则是未加载,因此多个类加载器可以加载同一个类。

2.2.2为对象分配内存

问题1:如何知道哪块是可用内存?
如果采用的垃圾收集器是Serial、ParNew等带Compact过程的收集器的时候(新生代创建对象,复制对象),内存是规整的,采用移动指针的方式进行分配。
如果采用的垃圾回收器是CMS这种采用标记清除法的时候(新生代对象移动到老年代),内存是不规整的,采用空闲列表的方式记录可用的内存区域。

问题2:多个线程同时创建对象的话,会出现并发问题,如何解决?

2.3初始化内存空间

内存分配完成之后,虚拟机会将分配空间内都初始化为零值(不包括对象头),如果使用TLAB分配,这一过程也可以提前至TLAB分配时进行。

2.4设置对象头

包括对象的哈希码、类元素信息、GC分代年龄等。这些信息都放置在对象头中。

2.5执行<init>方法

2.6对象回收

问题1:什么样的对象需要被回收?
无用的对象,即外部引用指向的对象。
问题2:怎么判断该对象已无引用?

问题3:什么对象可以成为GCRoot?
局部变量表中引用的对象,静态变量引用的对象,常量引用。
问题4:强引用,软引用,弱引用,虚引用的对象回收策略。

Object obj = new Object();      // 这个obj就是对象的强引用,只要有强引用存在,就不会被回收。
Object obj = new Object();                                                  // obj是强引用
SoftReference<Object> sf = new SoftReference<Object>(obj);      // sf是软引用
obj = null;                                    // 强引用置空
sf.get();//有时候会返回null    //垃圾回收时,如果内存空间不足,会回收软引用的对象。 
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有时候会返回null
wf.isEnQueued();//弱应用对象只能存活到下一次垃圾回收
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永远返回null
pf.isEnQueued();//返回是否从内存中已经删除
虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。
虚引用主要用于检测对象是否已经从内存中删除

各类引用使用场景请参考:https://www.cnblogs.com/yw-ah/p/5830458.html

2.7 对象死亡拯救

finalize

3.垃圾回收

3.1垃圾回收算法

3.2 分代垃圾回收和垃圾收集器

垃圾收集器
垃圾收集器

3.java监控

3.1 查看GC类型和GC日志

image.png

GC收集器默认类型是 Parallel Scanvage + Parallel Old

youngGC日志
fullGC日志

3.2 jmap

查看内存分配情况以及设置


jmap
heap配置
heap使用情况

3.3 jstat

查看内存使用情况以及gc次数


jstat
image.png
image.png

查看更多信息请参考:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jstat.html

3.4jstack (thread dump)

jstack 4170


jstack

3.5 jvisualvm & jsoncole

4.常见问题排查

4.1cpu占用过高

https://jingyan.baidu.com/article/4f34706e3ec075e387b56df2.html

4.2 内存泄漏排查

GC回收不掉

上一篇 下一篇

猜你喜欢

热点阅读