深入理解java虚拟机

一、JAVA虚拟机的组成

方法区、JAVA堆、虚拟机栈、本地方法区、程序计数器

1、方法区

1.1 方法区是线程共享的内存区域

1.2 存储已被虚拟机加载的类的信息,常量,静态变量和即时编辑器编译后的代码等数据;

1.3 有时候会成为永生代,该区很少发生垃圾回收,但是不代表不发生GC;

1.4 方法区里面有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该常量池具有动态性,也就是说常量不一定是编译时确定,运行时生成的常量也会存在这个常量池中;

2、JAVA堆

java堆也是所有线程共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例都会在这里创建,因此该区域经常会发生垃圾回收

3、虚拟机栈

3.1 也成栈内存,它为java方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接方法出口等信息;

3.2 虚拟机栈是线程私有的,它的生命周期与线程相同;

3.3 局部变量表存储的是基本数据类型returnAddress类型(指向一条字节码的指令)和对象引用;这个对象引用有可能是指向起始地址的一个指针,也有可能是代表对象的句柄或者与对象相关联的位置。局部变量表所需的内存空间在编译时确定;

3.4 操作数栈主要是用来存储运算结果以及运算的操作数。它不同于局部变量表通过索引来访问,而是压栈和出栈的方式;

3.5 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接;

3.6 动态链接是将常量池中的符号引用在运行期转化为直接引用;

4、本地方法区

本地方法区和虚拟机栈很类似,只不过本地方法区为native方法服务;

5、程序计数器

内存空间小,字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的字节码指令,分支、循环、跳转和异常处理和线程回复等功能都需要依赖这个计数器完成,该区域是唯一一个java虚拟机规范没有规定任何OOM情况的区域;

二、对象访问的方式

对象访问的方式有两种:句柄访问和直接指针访问

1.句柄访问主要是Java堆中划分一块句柄池,虚拟机栈中存放句柄池中的地址,句柄池中包括对象的实例数据和对象类型的数据的地址,基本分布如下图:

2..直接指针访问,就是虚拟机栈直接指向Java堆中的对象类型指针和对象的实例数据,然后对象类型指针在指向方法区中对象类型的实例数据,分布如下图:

三、JAVA垃圾回收机制(GC)

所谓垃圾回收,就是回收已经没有用的对象,判断一个对象有没有引用:

引用计数器、可达性分析算法

引用计数器:对象中添加一个引用计数器,每当有一个地方引用计数器就增加1,引用失效就减少1,计数器为0就不可用;缺点就在于无法处理对象直接相互引用的问题,因为相互引用以后无法使计数器为0,所以无法回收;

可达性分析算法(GC Root):当一个对象没有与任何引用链相连的时候,就可以对该对象进行回收;

下面是Java中GC Root对象使用的几个地方:

  • 虚拟机栈中的引用对象
  • 方法区中类静态属性引用
  • 方法区中常量引用对象
  • 本地方法中的JNI引用对象

引用的强弱分为:

  • 强引用:垃圾回收器永远不会回收掉的引用对象
  • 软引用:描述一些有用但非必需的对象,在系统将要溢出前,会把这些对象列入回收范围的第一次回收
  • 弱引用:来描述非必须的对象,但是它的引用比软引用更弱,弱引用对象关联的对象只能生存到下一次垃圾回收之前,垃圾回收器工作之时,无论内存是否足够,都会回收到相关联的对象
  • 虚引用:完全不会对对象构成影响,也无法通过虚引用来取得对象实例,存在的目地是为了一个虚引用对象被回收的时候收到一个系统通知

1.当内存空间不足的时候就需要触发GC,GC回收的时候采用的是分代收集的算法,主要分为年轻代老年代,接下来我们简单介绍一下这2种方式:

年轻代:当一个对象被创建的时候,内存分配首先分配在年轻代,大部分对象创建以后都不再使用,对象很快变得不可达,就是对象无用,由于垃圾是被年轻代清理掉的,所以被叫做Minor GC或者Young GC。

老年代:对象如果在年轻代存活了足够长的时间而没有被清理掉(即在几次Young GC后存活了下来),则会被复制到年老代,年老代的空间一般比年轻代大,能存放更多的对象,在年老代上发生的GC次数也比年轻代少。当年老代内存不足时,将执行Major GC,也叫 Full GC。

2.一个对象如何被Kill掉的流程:

1).当一个对象被创建的时候(new)首先会在年轻代的Eden区被创建,直到当GC的时候,根据可达性算法,看一个对象是否消亡,没有消亡的对象会被放入年轻带的Survivor区,消亡的直接被Minor GC Kill掉;

2).进入到Survivor区的对象也不是安全的,当下一次Minor GC来的时候还是会检查Enden和Survivor存放对象区域中对象是否存活,存活放入另外一块Survivor区域;

3).当2个Survivor区切换几次以后,会直接进入老年代,当然进入到老年代也不是安全的,当老年代内存空间不足的时候,会触发Major GC,已经消亡的依然还是被Kill掉;

原文地址:https://www.cnblogs.com/myRichard/p/11740959.html