1.java内存区域与溢出异常

一,java的运行时数据区分为本地方法栈,虚拟机栈,程序计数器,堆和方法区

①程序计数器,线程私有的,每个线程自己单独维护一个程序计数器,它主要是用于记录当前线程执行的下一条指令的地址

②虚拟机栈,线程私有,每个线程自己单独维护一个虚拟机栈,主要用于线程执行到方法时压入栈帧

③本地方法栈,和虚拟机栈基本 一样,只是执行的是本地方法时压入的栈帧

④堆,线程共享的,主要是用于为为大部分对象的创建提供开辟内存的空间

⑤方法区,线程共享的,主要是用于存放类元数据信息,其中包括运行时常量池,class文件中运行时常量池的数据及符号引用等信息就是存于此

二,对象在内存中的布局

①创建

首先,检查下当前要创建的类元数据信息是否加载到方法区中,如果未加载,则先启用类加载启对该类的class文件进行加载

接着,根据类元数据的信息,在堆内存中开辟对应大小的内存空间,这里开辟的方式有两种,一种是指针碰撞,就是规整内存中,指针的左边为已使用内存,右边为未使用内存,那么当分配内存的时候,只需要将改指针向右移动相应大小的内存空间即可,

一种是空闲列表法,是针对不规整的内存,那么虚拟机会维护一份列表,哪些空间是空闲,哪些空间是已使用的,那么从空闲列表中找出适合大小的内存给予分配,另外,在分配过程中 有可能涉及到并发问题,

也是两种解决方式,一种是cas+失败重试,另一种是tlab,thread local allote buffer,即为每个线程单独分配一份当前专门用于分配内存的空间,那么就不会涉及到竞争问题了,只有在分配给当前线程的空间不够 用了,才会涉及到同步问题

然后,初始化空间,赋"零值"

紧接着,设置对象头

最后,执行构造函数

②内存布局

一个对象在堆中的布局包括对象头,实例数据,填充对齐

对象头 又包括 MarkWord,类型指针及如果是数组的话还有数组的长度

③访问定位 

对对象的访问主要有两种方法,一种是句柄,一种是直接指针

句柄指的是通过维护一个句柄池,栈里面的地址指向的是句柄池中对应句柄的地址,再通过对应句柄里面 的地址指向堆中对应对象的地址,这种方法优点就是当对象地址 有变动的时候,栈里面的地址是不需要变的,只有句柄池里面对应的句柄里面的地址需要变,缺点就是需要两次寻址,直接指针指的就是栈里面的地址直接指向堆中对象的地址,优点就是一次定位,效率高 ,缺点就是一旦堆中对应对象的地址有变动,那么栈里面的地址信息就得跟着 变

三,溢出场景列举

①堆溢出,主要是在堆中的对象创建过多,又因为不恰当的引用得不到释放导致

②栈溢出,那么有可能就是栈中的栈帧过多或者局部变量表定义的变量过多

③方法区溢出,主要是加载到方法区中的类元数据过多,且卸载不了导致

④直接内存溢出,主要是调用了开辟堆外内存的方法过多导致

 

posted @ 2021-11-01 20:59  Kyhoon  阅读(24)  评论(0编辑  收藏  举报