Heap 堆
- 所有线程共享、生命周期与JVM进程相同
- 主要存放对象实例
- 没有空间且无法扩展时,OutOfMemeryError
- GC主要负责此区域的清理
- 空间最大的区域
Method Aread 方法区(PermGeneration 持久代)
- 所有线程共享、生命周期与JVM进程相同
- 主要存放类的信息、常量、静态变量即类变量、代码数据
类的二进制字节流文件加载后,根据类文件内容封装出的数据结构会存放在方法区
类连接准备阶段,给静态变量分配内存、初始化,也是在方法区
即时编译后的代码数据 - 没有空间且无法扩展时,OutOfMemeryError
- 垃圾回收
主要通过卸载类、清理常量实现,很少进行,可以选择不实现垃圾回收
JVM栈 线程栈
- 线程私有,每个线程各自拥有独立的栈,生命周期同其所在线程
- 描述了java方法执行的内存模型,由栈帧组成
每个方法被执行的时候创建一个栈帧,用于存储方法的局部变量表、操作栈、动态链接、方法出口等信息。
每个方法被调用直至完成的过程,就对应着一个栈帧在线程栈中入栈到出栈的过程。
每个方法的局部变量表大小在编译期就已经确定,包含了方法中所用到的所有局部变量(基本类型变量的值、复杂类型对象的引用、以及一个返回值地址类型returnAddress)。
除了long和double需要2个局部变量空间(Slot),其它类型只需1个。 - 如果线程请求的栈深度超过了虚拟机所允许的深度,StackOverflowError
- 当线程栈需要进行扩展,但JVM内存已经全部被占用,OutOfMemeryError
- 无需GC,线程执行完后,所有栈帧出栈,栈消失
本地方法栈
- 类似JVM栈,不过是当执行native方法时创建。
- 同JVM栈一样,抛出StackOverflowError和OutOfMemeryError
- 同JVM栈一样,无需GC
程序计数器
- 线程私有、每个线程各自拥有一个独立的、声明周期与线程相同
- 存放的是当前线程所执行的字节码的行号
操作系统在线程间切换时,通过程序计数器确定下一条需要执行的字节码指令。
字节码解释器工作时就是通过改变此计数器的值来选取下一条需要执行的指令。
顺序、循环、分支、跳转、线程恢复等基础功能都需要依赖这个计数器来完成。 - 只需较小的一块内存,不会抛出任何内存不足的异常
- 无需GC
各个版本的JMM
jdk 8
本章节转自:JVM在新版本的改进更新
简单总结:
- jdk 8 没有了Perm Gen,之前存储在Perm Gen中的常量池、静态变量,改存到堆里。
- 新开辟了一个空间MetaSpace,用来存储类的元数据信息,此空间逻辑上不属于JVM的虚拟内存空间,不占用虚拟机内存,不受虚拟机内存限制,而是使用本地内存,可以更多的使用本地内存
- PermSize参数不再有意义,新增 MetaspaceSize 参数
- 有Perm Gen时,每个JVM各自拥有一个Perm Gen,每个JVM进程的类元数据存放在各自的Perm Gen中,如果多个JVM使用了相同的jar包,比如两个项目都用了fast-json开源包,那么每个进程都会有一份此jar包的类信息,造成重复和资源浪费。而取消了Perm Gen后,MetaSpace逻辑上是独立于JVM虚拟内存的,而是存储在本地内存中,这样多个项目使用了相同的jar时,就可以共用MetaSpace里的一份类信息即可。
详解:
JDK 1.7 及以往的 JDK 版本中,Java 类信息、常量池、静态变量都存储在 Perm(永久代)里。类的元数据和静态变量在类加载的时候分配到 Perm,当类被卸载的时候垃圾收集器从 Perm 处理掉类的元数据和静态变量。当然常量池的东西也会在 Perm 垃圾收集的时候进行处理。
JDK 1.8 则将类元数据放到本地内存中,另外,将常量池和静态变量放到 Java 堆里。HotSopt VM 将会为类的元数据明确分配和释放本地内存。在这种架构下,类元信息就突破了原来 -XX:MaxPermSize 的限制,现在可以使用更多的本地内存。这样就从一定程度上解决了原来在运行时生成大量类的造成经常 Full GC 问题,如运行时使用反射、代理等。