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

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

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

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虚拟机-自动内存管理机制)