《深入理解JVM》这本书写的很不错,以下是我在阅读时随手记录的,大家可以一看。
运行时数据区
- 方法区(所有线程共享)
- 堆(所有线程共享)
- 虚拟机栈(单个线程私有)
- 本地方法栈(单个线程私有)
- 程序计数器 (单个线程私有)
程序计数器
记录线程内部的指令执行顺序,因为java多线程是通过线程流转切换并分配处理器执行时间的方式,所以每个线程自己需要一个可以记录自己线程内部指令执行过程的计数器。负责去取下一条需要执行的字节码指令。
虚拟机栈
存放编译器各种可知的基本数据类型,每一个Java方法执行时的各种信息也存放在这里,方法局部变量,方法出口等信息。
本地方法栈
类似虚拟机栈,存放Native方法的信息,在有的虚拟机中会将虚拟机栈和本地方法栈合二为一。
Java堆
目前而言,大部分的Java对象都是在堆上分配内存的。还有一小部分,可以采用逃逸分析,标量替换,栈上分配等手段来减轻回收压力,不在堆上分配内存。
这一部分空间是垃圾收集器管理的主要区域。
方法区
用于存储被虚拟机加载类的信息(类名,字段描述、类型等),常量,静态变量,即时编译后的代码等数据。另一个别名,永久代。这一部分的内存回收非常苛刻,主要针对常量池回收和对类型的卸载。在JDK1.8中,常量池被转移至Java堆中,永久代也被迁移至一个叫做元空间MetaSpace的位置,位于本地内存中。
采用指针碰撞还是维护空闲列表来给对象分配空间,取决于采用的垃圾收集器是否带有压缩整理功能
可以通过给线程分配TLAB的方法,避免分配时线程不安全,只有在需要重新分配TLAB或者TLAB空间不足时,再做同步处理。当然也可以通过使用CAS加失败重试,CAS,可以看这篇文章
对象的访问定位
使用栈上的reference引用,指向对象的,目前有两种方式去访问一个对象。
- 使用句柄访问即reference(对象句柄地址)->(Java堆)句柄池(对象实例数据指针+对象类型数据指针),实例数据指针->Java堆实例数据,类型数据指针->方法区实例类型数据
- 直接访问,reference->对象类型实例数据+实例类型数据指针,实例类型数据指针->指向方法区中的实例类型数据
元空间
- 实际上持久代已经被删除,取而代之的是元空间,位于本地内存中。
- 它可以通过-XX:MetaspaceSize和-XX:MaxMetaspaceSize来进行调整