简单的JVM图(画的巨丑)
所有的扩展如果不足就会产生StackOverflowError或者OutOfMemoryError
- java虚拟机栈
大部分情况下指虚拟机栈里的局部变量表,局部变量表里存放了各种基本数据类型,还有对象引用,但是并不是对象本身,有可能只是引用地址,或者句柄和指针
- 本地方法栈
虚拟机栈使用的是java方法服务,本地方法栈是为Native方法服务,有的虚拟机甚至将两者合二为一
- java堆
java堆又被称为GC堆,java堆中经常划分新生代,老年代,永久代等,java堆可以划分多个线程私有的缓冲区,无论哪个区域都存储的是对象实例
java堆可以可扩展,当前主流java虚拟机都是可扩展的(-Xmx,-Xms)
- 方法区
各个线程共享区域
在JVM启动时被创建,并且物理内存可以不连续
大小可以固定也可以是动态扩展的
方法区的大小决定了系统可以保存多少个类
会随着JVM的关闭而释放这一区域的内存
jDK8以前方法区有永久代概念在后面取消了这个概念
在JDK7
-XX:Permsize 设置永久代初始分配空间
-XX:MaxPermsize
而JDK8以后变成了元空间,元空间更多是保存在本地内存中并不在虚拟机中
- 运行时常量池
运行时常量池是在方法区里的一部分,用于存放编译期间各种字面量和引用,这部分在类加载后放到常量池中
- 程序计数器
程序技术器只是一个int值,记录当前执行到哪个位置上
对象是如何产生的?
java语言如果是比较规整的
就会采用指针碰撞的方式
什么是指针碰撞(笔者自己的理解方式)就是将指针向空闲的方向移动一段与对象大小一致的距离如果不能换只能通过空闲列表方式来换
但是修改指针的方式并不线程安全,比如正在挪动指针的时候对象B又使用了原来的内存进行分配,有两种解决方案,一种是通过CAS的方式维护线程安全,或者预先分配一个缓冲区在不同的线程,本地缓冲区使用完了再分配新的。
这些操作做完了之后再对对象进行必要的设置,比如hashCode还有GC年代,或者是是否使用了偏向锁。
对象的内存布局?
对象头,实例数据,对齐填充
对象头包含两类数据第一类存储运行数据,第二类存储类型指针
运行数据包括什么?
hashcode,GC年代,锁状态标识,锁,偏向线程的ID
类型指针是通过这个指针来确定对象是哪个实例,但是并不是所有对象都有类型指针
实例数据部分,父类继承的字段也是要被保存下来的所有的字段都要保存下来,子类较短的字段会插到父类当中去
对齐填充不是必须要有的,一般来讲任何对象大小都是8的整数倍,缺失的话需要对齐填充
Java对象的大小为什么必须是8字节的整数倍?
上面的对象A、B、C我们假设的大小是8字节、16字节、8字节;共同点你可能发现了,他们都是8字节的倍数,其实Java对象的大小就必须是8字节的整数倍,如果没有这个条件,上面说的64/32最大公约数说法也不成立。
当然除了为了支持上面这些功能外,另外还有的就是因为现在大多数计算机都是高效的64位处理器,顾名思义,一次能处理64位的指令,即8个字节的数据,HotSpot VM的自动内存管理系统也就遵循了这个要求,这样子性能更高,处理更快。
对象的访问定位?
reference 是一个对象的引用,主流的访问是句柄和指针
未完待续