一、虚拟机的基本结构

Java 虚拟机的基本结构如图:

jvm.png

绿色代表所有线程共享区域,黄色代表每个线程的私有区域


1、方法区:或者叫永久代,包括类信息和运行时常量信息等。

  • 类信息:从文件或网络系统中加载的 class 信息。
  • 静态变量、即时编译器编译后的代码。
  • 运行时常量池:包括字符串字面量、数字常量。
    • 整数类型会提前缓存[-128,127]之间的数字。
    • String的intern()方法会去常量池找相同的字符串并返回。

2、Java 堆:是 Java 最主要的内存工作区域,几乎所有的对象实例数组都在堆上分配。

3、Java 栈:是线程私有的。描述的是Java 方法 执行的内存模型,保存着局部变量表、操作数栈、帧数据区等信息。

4、本地方法栈:为 Native 方法的执行服务。

5、程序计数器:主要代表当前线程所执行的字节码行号指示器。字节码解释器工作时,通过改变这个计数器的值来选取下一条需要执行的字节码指令

二、Java 栈

Java 栈是一块先进后出的数据结构,方法只有入栈出栈

它所操作的对象是栈帧。每个方法执行的时候都会创建一个栈帧,它保存着局部变量表、操作数栈、帧数据区等信息。

当方法被调用时,它对应的栈帧就会入栈,当方法返回时,栈帧就会出栈。

stack.png

三、Java 堆

几乎所有的对象都放在 Java 堆中。堆是自动管理的,通过垃圾回收,对象会被自动释放。

heap.png


堆大小 = 新生代 + 老年代
新生代 = eden + from survivor + to survivor

eden : from : to = 8 : 1 : 1
new : old = 2 : 1

(比例是可以通过参数调整的)
  • eden(伊甸园):大多数新对象会在 eden 中产生,但是大对象会直接进入老年代。“朝生夕死” 的对象占大多数,很多新对象用完一次就可以被回收。
  • from/to survivor(幸存者):JVM 仅会使用 eden 和一个幸存者区(这里假设是 from)进行对象分配,另一个幸存者(这里假设是 to)区则会保持清空。在一次 GC(minor GC)之后,幸存的对象会被复制到 to 区域。如果在幸存者区(from)中的对象年龄达到一定阈值就会直接进入老年代; 下次对象分配的时候,eden 和 to 会被使用,而 from 保持清空,循环上一步的过程。
  • 老年代:老年代发生的 GC 是 Full GC(或者叫 stop the world),这里的 GC 动作不像新生代那么频繁。在每次 minor GC 之前,系统会判断老年代的空闲连续空间是否大于新生代所有对象之和,如果这个条件成立,那么MinorGC可以确保是安全的。如果不成立,则虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试这进行一次MinorGC,尽管这次MinorGC 是有风险的;如果小于,或者 HandlePromptionFailure 设置不允许冒险,那这是也要改为进行一次Full GC。