https://docs.oracle.com/javase/7/docs/
JDK java de
JRE java runtime environment
JVM java virtual machine
内存溢出模拟
java.lang.OutOfMemoryError: java heap space
List<Demo> list = new ArrayList<>();
while(true){
list.add(new Demo());
}
运行时数据区
线程独占区
程序计数器
1)程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器
2)程序计数器处理线程独占区
3)如果线程执行的是java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的是native方法,这个计数器的值为undefined
4)此区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域
java虚拟机栈
1)虚拟机栈描述的是java方法执行的动态内存模型
2)栈帧
每个方法执行,都会创建一个栈帧,伴随着方法从创建到执行完成。用于存储局部变量表,操作数栈,动态链接,方法出口等
3)局部变量表
- 存放编译器可知的各种基本数据类型,引用类型,returnAddress类型
- 局部变量表的内存空间在编译期完成分配,当进入一个方法时,这个方法需要在帧分配多少内存时固定的,在方法运行期间是不会改变局部变量表的大小
4)大小
- 当栈不够时抛出StackOverflowError
- 当栈足够大时,这个时候影响程序的可能就不是栈的大小了,而会是内存的大小,有可能会抛出OutOfMemory错误
栈溢出模拟
java.lang.StackOverflowError
public class StackTest{
private void test(){
System.out.println("方法执行");
}
public static void main(String[] args){
new StackTest().test();
}
}
本地方法栈
虚拟机栈为虚拟机执行java方法服务
本地方法栈为虚拟机执行native方法服务
其它与java虚拟机栈类似
线程共享区
Java堆 最大的一块内存区域
存放对象实例
垃圾收集器管理的主要区域
新生代、老年代、Eden空间
OutOfMemeryError
方法区
1)存储虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据
类信息:
- 类的版本
- 字段
- 方法
- 接口
2)方法区和永久代
3)垃圾回收在方法区的行为
在方法区做垃圾回收效率会很低,根据存放的内容可推断
4)异常的定义
OutOfMemoryError
运行时常量池 属于方法区
String 的问题
intern() 方法
直接内存
nio中会使用到
对象创建
给对象分配内存
- 指针碰撞 (规整的内存快)
- 空闲列表
内存分配线程安全性问题
- 线程同步(加锁)
- 本地线程分配缓冲(TLAB),缓冲使用完了再用加锁的方式增加内存
初始化对象
执行构造方法
对象的结构
https://www.cnblogs.com/zhengbin/p/6490953.html
- Header(对象头)
- 自身运行时数据(Mark Work)
- 哈希值(object提供了本地方法hashCode获得对象hash值)
- GC分代年龄(为分代回收算法服务)
- 锁状态标志
- 线程持有的锁
- 偏向线程id
- 偏向时间戳
- 类型指针
指向对象元数据的指针
java数组的话对象头还会存储数组长度
- 自身运行时数据(Mark Work)
- InstanceData(数据实例)
相同宽度的字段会存放到一起,比如Longs/doubles shorts/chars
所以在父类定义的变量 可能会出现在子类之前 - Padding(对齐填充)
对齐填充不是必然存在的,没有特别的含义,它仅起到占位符的作用。
由于 HotSpot VM 的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,也就是说对象的大小必须是 8 字节的整数倍。对象头部分是 8 字节的倍数,所以当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。
对象访问定位
- 使用句柄
好处:栈中引用地址不需要更改,更改的只是句柄池中的地址 - 直接指针
好处:减少了一次寻址过程性能高
两种方式使用哪种取决于虚拟机实现
hotspot使用的是直接指针的方式