参考以下文章
Java 虚拟机Java 虚拟机是什么深入理解java虚拟机JVM内存模型总结Java内存区域(运行时数据区域)和内存模型(JMM)

1 虚拟机是一个普通进程。
2 类加载器加载class文件。
3 执行引擎用来执行class文件中的字节码指令
4 虚拟机在执行过程中,要分配内存创建对象。当这些对象过时无用了,必须要自动清理这些无用的对象。垃圾收集器负责清理对象回收内存的任务由。。

一.Java运行时数据区域

unsafe java 内存_Java


下图是 JDK8 之后的 JVM 内存布局。

unsafe java 内存_unsafe java 内存_02


unsafe java 内存_Test_03

1.程序计数器(PC Register)

是最小的一块内存区域,它的作用是当前线程所执行的字节码的行号指示器,在虚拟机的模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、异常处理、线程恢复等基础功能都需要依赖计数器完成。

2.虚拟机栈(JVM Stack)

unsafe java 内存_引用计数_04


描述的是java方法执行的内存模型:每个方法被执行的时候都会创建一个"栈帧",用于存储局部变量表(包括参数)、操作栈、方法出口等信息。**每个方法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。**声明周期与线程相同,是线程私有的。

栈帧由三部分组成:

(1)局部变量区:被组织为以一个字长为单位、从0开始计数的数组。放编译期可知的各种基本数据类型,引用类型,局部变量表的大小在编译期便已经可以确定,在运行时期不会发生改变。

(2)操作数栈:一个以字长为单位的数组。而是通过入栈和出栈来访问的,可以看作为临时数据的存储区域。

(3)帧数据区:支持常量池解析的数据、正常方法返回以及异常派发机制。

3.本地方法栈(Native Stack)

unsafe java 内存_Test_05

本地方法栈为虚拟机执行native方法服务

4.堆(Heap)

unsafe java 内存_Test_06


该内存区域存放了对象实例及数组(但不是所有的对象实例都在堆中)

是垃圾收集的主要区域(“GC 堆”)。

现代的垃圾收集器基本都是采用分代收集算法,其主要的思想是针对不同类型的对象采取不同的垃圾回收算法。可以将堆分成两块:

新生代(Young Generation)

老年代(Old Generation)

可以通过 -Xms 和 -Xmx 这两个虚拟机参数来指定一个程序的堆内存大小,第一个参数设置初始值,第二个参数设置最大值。

java -Xms1M -Xmx2M HackTheJava

5.方法区(Method Area)

用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。元空间存储类的元信息,静态变量和常量池等放入堆中。

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

运行时常量池是方法区的一部分。
Class 文件中的常量池(编译器生成的字面量和符号引用)会在类加载后被放入这个区域。
除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。

7.直接内存(Direct Memory)

直接内存并不是虚拟机内存的一部分,也不是Java虚拟机规范中定义的内存区域。jdk1.4中新加入的NIO,引入了通道与缓冲区的IO方式,它可以调用Native方法直接分配堆外内存,这个堆外内存就是本机内存,不会影响到堆内存的大小.

二.垃圾回收

unsafe java 内存_引用计数_07

1. 引用计数算法

为对象添加一个引用计数器,当对象增加一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收。
在两个对象出现循环引用的情况下,此时引用计数器永远不为 0,导致无法对它们进行回收。正是因为循环引用的存在,因此 Java 虚拟机不使用引用计数算法。

public class Test {

    public Object instance = null;

    public static void main(String[] args) {
        Test a = new Test();
        Test b = new Test();
        a.instance = b;
        b.instance = a;
        a = null;
        b = null;
        doSomething();
    }
}

在上述代码中,a 与 b 引用的对象实例互相持有了对象的引用,因此当我们把对 a 对象与 b 对象的引用去除之后,由于两个对象还存在互相之间的引用,导致两个 Test 对象无法被回收。

2. 可达性分析算法

以 GC Roots 为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。

Java 虚拟机使用该算法来判断对象是否可被回收,GC Roots 一般包含以下内容:

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

待补充……