ref: https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/jvm/Java内存区域.md
- java虚拟机内存区域分布
- JDK1.8之前
- JDK1.8
- JDK1.8之前
以下6个是Java虚拟机运行时数据区:
-
程序计数器
- 当前线程的字节码的行号指示器;
- 每个线程都需要有一个独立的程序计数器;
- 不会抛出OutOfMemoryError异常,其他5个都会。。。。
-
Java虚拟机栈
- 为虚拟机执行java方法(字节码)服务;
- 线程私有的:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数帧、方法出入口;
- 局部变量表:基本数据类型、对象引用和returnAddress类型;
-
本地方法栈
- 为虚拟机使用到的native方法服务;
-
Java堆
- 被所有线程共享;
- 所有对象实例以及数组都要在堆上分配;
- 其中有进一步划分,为了更好的垃圾回收;
- Classloader加载一个类并把类型信息保存到方法区后,会创建一个Class对象,存放在堆区的,不是方法区,它为程序提供了访问类型信息的方法;Hotspot中Class类是存储与方法区的;
-
方法区
- 线程共享的;
- 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据;
- 此处的垃圾回收主要针对常量池的回收和对类的卸载;
-
运行时常量池
- 方法区的一部分;
- Class文件中除了有类的版本、字段、方法、接口等信息外,还有一项信息是常量池,用于存放编译器生成的各种字面量和符号引用;
- 直接内存
- 不是虚拟机运行时数据区的一部分;
- NIO(New Input/Output)类通过Native函数库直接分配堆外内存,通过存储在java堆中的DirectByteBuffer对这块内存的引用进行操作;
- 可以提高性能,避免了在Java堆和Native堆来回复制数据;
- 虽然不受Java堆大小的限制,但受到本机内存的限制;
-
对象创建过程
- 内存分配
- 并发问题
- 内存分配
-
对象的内存布局
- Hotspot 虚拟机的对象头包括两部分信息
- 第一部分用于存储对象自身的自身运行时数据(哈希码、GC 分代年龄、锁状态标志等等),
- 另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是那个类的实例。
- 实例数据部分是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。
- Hotspot 虚拟机的对象头包括两部分信息
-
对象的访问定位
- Java 程序通过栈上的 reference 数据来操作堆上的具体对象;
1, 使用句柄访问:如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息;
- 直接指针访问: 如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而 reference 中存储的直接就是对象的地址。
- 这两种对象访问方式各有优势。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。使用直接指针访问方式最大的好处就是速度快,它节省了一次指针定位的时间开销。
- Java 程序通过栈上的 reference 数据来操作堆上的具体对象;