jvm中对象的创建过程
虚拟机遇到new指令时:
首先检查方法区中是否加载了类信息,如果没有加载,则先加载类信息
类加载后,为对象分配内存,大小在加载类信息时确定
如果对内存是规整的,即垃圾回收后整理内存碎片合并成一块未使用的区域则分配内存就是将记录内存位置的指针移动相应大小的一段距离(指针碰撞)
如果内存不规整,即垃圾回收后没有整理内存碎片,那么需要维护一个列表,记录内存碎片大小,分配空间时选择可用的一块(空闲列表)
同时分配内存时存在线程安全问题,即分配对象a的内存,指针还没有变化,对象b又用了该指针分配内存,解决这个问题有两种方式
1.CAS(旧值C,期望值A,新值s,当A=C时说明C值没有修改过,然后让C=S 这种方法可能引发ABA问题)+失败重试
2.把内存分配根据不同线程,将堆划分为不同的空间(TLAB 本地线程分配缓冲),哪个线程分配内存,就在哪个线程的TLAB上划分,当TLAB用完,分配新的TLAB时,采用同步方法
对象分配内存后设置头信息(对象头信息后面再做介绍)
头信息设置完成后执行初始化方法,至此对象创建完成
对象的访问定位:
java程序,通过栈上的reference数据操作堆上的具体对象,目前访问java对象有两种主流方式
1.句柄
堆中划分一块区域作为句柄池,reference中存储句柄地址,句柄中存储指向某一对象的指针
2.直接指针
reference中存储指向某一对象的指针
使用句柄的优点:垃圾回收时会频繁移动对象,使用句柄,reference中的句柄地址无需改变,只需要修改句柄中指针指向的位置即可
使用直接指针的优点:访问对象效率更快,因为中间没有了定位句柄的过程,但是垃圾回收时,不但要修改指针指向的位置,还要修改reference的值
java虚拟机栈溢出OOM,本地方法栈溢出OOM
栈内存大小=操作系统内存大小-堆(xmx)-方法区(MaxPermSize)
当程序涉及多线程时,如果每个线程的栈空间越大,则可创建的线程数量越少,如果线程数过多,则会耗尽栈内存导致溢出
此时应该扩大系统内存,或者减少堆大小或者减少每个线程分配的栈容量(xss)