美文网首页
JVM虚拟机-自动内存管理机制

JVM虚拟机-自动内存管理机制

作者: aeborah | 来源:发表于2021-08-24 15:48 被阅读0次

    一.虚拟机的运行时数据区

    包括:方法区,堆,栈(本地方法栈,虚拟机栈),程序计数器

    1.程序计数器(线程私有):

    可以当作一个行号指示器,通过数字改变来选取下一条需要执行的指令,分支,循环,跳转,异常等都是依赖计数器来完成的。

    虚拟机的多线程是通过线程轮流切换来实现,所以在某一个确定的时刻只能执行一个线程中的一条指令,这就要求程序计数器是线程私有的来保证互不干扰,并且切换以后知道下一条要执行什么指令。

    2.虚拟机栈(线程私有):

    线程私有,生命周期与线程相同。
    每个方法执行的时候回创建一个栈帧,来存储局部变量表,操作数栈,动态链接,方法出口。
    局部变量表存储各种基本数据类型和引用数据类型的地址。

    3.本地方法栈(线程私有):

    与虚拟机栈的区别在于,虚拟机栈为虚拟机执行java方法,本地方法栈为虚拟机使用到的native方法服务。有的虚拟机会将二者合二为一。

    3.堆(线程共享):

    存放对象实例。
    可以存在物理上不连续的内存空间。如果堆中没有内存完成实例分配并且堆无法扩展,就会产生OutOfMemoryError。

    4.方法区(线程共享):

    存放类信息,常量,静态变量,即时编译器编译后的代码等。
    可以存在物理上不连续的内存空间。如果堆中没有内存完成实例分配并且堆无法扩展,就会产生OutOfMemoryError。

    5.运行时常量池:
    6.直接内存:

    直接内存不是jvm数据区的一部分,但是这部分内存会被频繁调用。
    在NIO中,阴图一种基于通道和缓冲区的io方式,可以使用native函数库直接分配堆外内存,因此在配置虚拟机参数的时候,如果忽略直接内存也有可能导致OutOfMemoryError。

    二.虚拟机的使用方法

    1.对象的创建:

    代码层面中的new一个对象,一般在jvm中会有以下几个操作:
    首先检查这个指令的参数是否能在常量池中找到这个类的符号引用。。。。
    然后分配内存,分配内存有两种方法,如果堆在内存中绝对规整,可以通过指针碰撞(Bump the Pointer)的方法,就是将指针挪动和对象大小相等的一段距离;第二种是通过空闲列表(Free List),维护一个记录记录内存块的列表,来分配实例。
    但是为了保证并发安全,可能需要同步,或者采用cas方法进行失败重试,或者直接采用本地线程分配缓冲(Thread Local Allocate Buffer)。即给每个线程直接预先分配号一段内存空间,只有内存空间不足的时候才需要同步锁定来分配新的TLAB。

    2.对象的内存布局:

    对象头,实例数据,对象填充。
    对象头一部分包含哈希码,GC分代年龄,锁状态,偏向线程ID等等。另一部分是类型指针,指向它的类元数据的指针。
    实例数据是有效存储信息。
    对象填充是占位符,自动内存管理可能需要对象地址必须是8字节的整数倍,所以需要对象填充补全。

    3.对象的访问定位

    定位一个堆上的具体对象需要通过栈上的reference数据。定位的方法取决于虚拟机的实现,目前主流的访问方式有使用句柄直接指针两种方式。
    句柄中包含对象实例数据和类型数据各自的具体地址信息的指针。
    直接指针里则直接存放对象实例数据地址和类型数据的指针。

    三.OutOfMemoryError

    1.堆溢出

    OutOfMemoryError:Java heap space
    当对象数量过多的时候,可能会产生这种情况,在这种情况下,重点确认内存中的对象是否必要,也就是分清楚到底是内存泄漏还是内存溢出,如果是发现有导致垃圾收集器无法收集的时候,就是内存泄漏,修改代码即可,如果不存在泄漏也就是对象都是必须的,那就考虑修改虚拟机的堆参数(-Xmx,-Xms),增大堆内存,或者是查看是否有某些对象生命周期过长或者持有状态时间过长,尝试减少内存消耗。

    2.虚拟机栈和本地方法栈溢出

    栈容量一般通过-Xss设定
    在单个线程下,如果栈帧过大或者虚拟机栈容量太小,都容易出现stackoverflowerror。
    但是还有另一种情况,如果不限于单线程,通过不断建立线程的方式会产生内存溢出异常。在这种情况下,为每个线程的栈空间分配的越大越容易异常,因为操作系统分配给每个进程的内存是由限制的,比如32位虚拟机限制为2GB,减去最大堆容量和最大方法区容量以后剩下的就是栈容量,如果每个栈空间越大,可建立的栈数量就越小,就越容易出现outofmemory,因此如果线程数过多产生内存溢出的时候,在无法减少线程数的情况下,可以考虑减少最大堆和栈容量的方式来换取过呢更多的线程。

    3.方法区和常量池溢出

    permGen space
    https://blog.csdn.net/jwandbj/article/details/77874031
    -XX:permSize来限制常量池容量。
    经常大量产生class的类中要特别注意回收状况。

    4.本机直接内存溢出

    -XX:MaxDirectMemorySize指定,如果不指定,则默认与java堆的最大值一样,直接内存溢出异常没有特殊的标记,所以当发现dump文件很小,又引用了NIO的时候可以考虑是否是直接内存溢出了。

    OOM的几种情况及解决方案:

    https://zhuanlan.zhihu.com/p/79355050

    相关文章

      网友评论

          本文标题:JVM虚拟机-自动内存管理机制

          本文链接:https://www.haomeiwen.com/subject/gvjdiltx.html