通俗易懂的JVM详解

1、运行流程

java程序经过一次编译之后,将java代码编译为字节码也就是class文件,然后在不同的操作系统上依靠不同的java虚拟机进行解释,最后再转换为不同平台的机器码,最终得到执行
通俗易懂的JVM详解_第1张图片
通俗易懂的JVM详解_第2张图片

2、JVM基本结构

通俗易懂的JVM详解_第3张图片
从这个结构不难看出,class文件被jvm装载以后,经过jvm的内存空间调配,最终是由执行引擎完成class文件的执行

3、内存空间

方法区:所有线程共享,里面存有常量池、静态变量、类信息

Java堆:所有线程共享,里面存有该虚拟机的所有对象/实例,可以想象你的一个系统会产生很多实例,因此java堆的空间也是最大的。如果java堆空间不足了,程序会抛出OutOfMemoryError异常。

Java栈:线程私有,主要存放方法的局部变量,它的生命周期与线程相同,一个线程对应一个java栈,每执行一个方法就会往栈中压入一个元素,这个元素叫“栈帧”。递归如果深度很深,就会执行大量的方法,方法越多java栈的占用空间越大,程序会抛出StackOverflowError异常

本地方法栈:和java栈类似,只不过它是用来表示执行本地方法的,本地方法栈存放的方法调用本地方法接口,最终调用本地方法库,实现与操作系统、硬件交互的目的。jni

PC寄存器:这时我们的类已经加载了,实例对象、方法、静态变量都去了自己改去的地方,那么问题来了,程序该怎么执行,哪个方法先执行,哪个方法后执行,这些指令执行的顺序就是PC寄存器在管,它的作用就是控制程序指令的执行顺序。

执行引擎:就是根据PC寄存器调配的指令顺序,依次执行程序指令。

内存泄露:是部分内存已经使用完毕,且未来不会再使用了,预期是被系统回收,但是此时又被另一个长生命周期的对象所持有(强引用),造成系统一直无法回收。而且一旦长时间运行 app,内存泄漏不断堆积,将会消耗系统所有的内存,从而造成内存不足(内存溢出)导致app crash!

4、垃圾回收机制GC

1、引用计数算法(在JDK1.2之前)

每个对象添加一个引用计数器,每被引用一次,计数器加1,失去引用,计数器减1,当计数器在一段时间内保持为0时,该对象就认为是可以被回收得了。(在JDK1.2之前,使用的是该算法)

缺点:当两个对象A、B相互引用的时候,当其他所有的引用都消失之后,A和B还有一个相互引用,此时计数器各为1,而实际上这两个对象都已经没有额外的引用了,已经是垃圾了。但是却不会被回收

2、可达性分析算法

该算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,从一个节点GC ROOT 开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点

目前java 中可作为GC Root 的对象有:

  1. 虚拟机栈中引用的对象(本地变量表)
  2. 方法区中静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中引用的对象(Native Object)

1) 、JDK 1.2之后,对引用进行了扩充,引入了强、软、若、虚四种引用,被标记为这四种引用的对象,在GC时分别有不同的意义

强引用(Strong Reference)

就是为刚被new出来的对象所加的引用,它的特点就是,永远不会被GC,除非显示的设置null,才会GC

软引用(Soft Reference)

内存溢出之前进行回收。如果JVM内存并不紧张,这类对象可以不被回收,如果内存紧张,则会被回收。既然被引用为软引用的对象可以回收,为什么不去回收呢?因为Java中存在缓存机制的

弱引用(Weak Reference)

弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。
弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器

虚引用(Phantom Reference)

虚引用是每次垃圾回收的时候都会被回收。虚引用主要用于检测对象是否已经从内存中删除。

2)、回收算法

  1. 标记-清除算法
    标记所有的需要回收的对象;标记完成后进行统一的回收带有标记的对象占据的内存空间。在存活对象比较多的情况下极为高效
    缺点是标记清除之后会产生大量不连续的内存碎片,当程序在运行过程中需要分配较大对象时,无法找到足够的连续内存而造成内存空间浪费。

  2. 复制算法
    复制算法是将内存容量划分为大小相等的两块,每次只使用其中的一块。当一块内存用完之后,就将还存活的对象复制到另一块上面,然后再把已使用的内存空间一次性清理。这样使得每次都对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只是这种算法的代价就是内存为原来的一倍

  3. 标记-整理算法
    标记整理算法不仅对不存活的对象进行清除,还对存活的对象进行重新整理,因此不会产生内存不连续的现象。解决了内存碎片的问题。
    缺点是执行时间会延长

3)、现在一般都是不同状态下的对象,按不同的算法回收

  1. 新生代
    一般情况下新生成的、朝生夕亡、生命周期短的对象一般都是首先存放在新生代里面。
    适用回收算法:复制算法

  2. 老年代
    老年代一般存放的是一些生命周期较长的对象,比如在新生代中经历来了n次垃圾回收后仍然存活的对象都进入了老年代。
    适用回收算法:标记整理或标记清除

  3. 永久代
    永久代主要存放静态文件,如java类,方法等,永久代对垃圾回收没有显著影响。

你可能感兴趣的:(源码,android)