JVM内存管理
- JVM内存结构
在Java虚拟机规范中将Java运行时数据划分为6种
PC寄存器数据
保存当前执行的程序的内存地址
Java栈
Java栈总是和线程关联,每当创建一个线程时,JVM就会为这个线程创建一个对应的Java栈,这个栈中又会含有多个栈帧,这些栈帧是与每个方法关联起来,每运行一个方法就创建一个栈帧,每一个栈帧会含有一些内存变量,操作栈和方法返回值等信息。
堆
堆是存放Java对象的地方,是JVM管理Java对象的核心内存区域,每个存储在堆中的Java对象都是这个对象的类的一个副本,它会复制包括继承自它父类的所有非静态属性
方法区
JVM方法区用于存储类结构信息的地方,在Java堆中的永久区内,在启动程序后一段时间就固定饿了
本地方法栈
是为JVM运行Native方法准备的空间,由于很多Native方法是C语言实现的,所以也叫C栈
运行时常量池
代表运行时每个class文件中的常量表
- JVM内存分配策略
通常的内存分配策略
1、静态内存分配:指在程序编译时就能确定每个数据在运行时刻的存储空间需求
2、栈式内存分配(动态存储分配):由一个类似堆栈的运行栈来实现,程序对数据区的需求在运行时才能够为其分配内存。栈式内存分配按照先进后出的原则进行分配
3、堆内存分配:当程序真正运行到相应的代码时才知道空间大小
Java中内存分配详解
Java栈:其分配是和线程绑定在一起的,当我们创建一个线程时,JVM会为这个线程创建一个新的Java栈,一个线程的方法的调用和返回对应于这个Java栈的压栈和出栈。当线程激活一个Java方法时,JVM就会在线程的Java堆栈里新压入一个帧,这个帧自然成了当前帧。在此方法执行期间,这个帧用来保存参数、局部变量、中间计算过程和其他数据。栈中主要存放一些基本的数据类型和对象句柄(引用)。存取速度比堆快,仅次于寄存器,栈数据可以共享。静态分配内存,编译时确定生存期大小。
Java堆:每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用程序所有的线程共享。Java中分配堆内存是自动初始化,所有对象的存储空间都是在堆中分配的,但是这个对象的引用在堆栈中分配。可动态分配内存大小、运行时分配内存。
即创建一个对象时,堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是指向这个堆对象的指针(引用)而已。
- JVM内存回收策略
静态内存分配和回收
Java中静态内存分配指在Java被编译时就已经能够确定需要的内存空间,当程序加载时系统把内存一次性分配给它。这些内存只有在程序结束时才被收回。
动态内存分配和回收
Java中对象的内存空间是动态分配的,就是在程序执行时才知道要分配的存储空间大小,只有等到对象不再使用时才会被回收。
如何检测垃圾
只要某个对象不再被其他活动对象引用,那么这个对象就可以被回收。活动对象指能够被一个根对象集合到达的对象。
根对象集合:
1、方法中局部变量区中对象的引用
2、Java操作栈中的对象引用
3、常量池中对象引用
4、本地方法中持有的对象引用
5、类的Class对象
基于分代的垃圾收集算法
- Young区 分为Eden区和两个Survivor区,其中新创建的对象都在Eden区,当Eden区满后触发minor GC 将Eden区仍然存活的对象复制到Survivor区中,另外一个Survivor区中的存活对象也复制到这个Survivor中,保证始终有一个Survivor区是空的。
- Old区存放的是Young区的Survivor满后触发minno GC后仍然存活的对象,当Eden区满后将对象存放到Survivor区中,如果Survivor中仍然存放不下这些对象,GC收集器会将这些对象直接存放到Old区。如果Survivor区中对象足够老,也直接存放到Old区。如果Old区也满了,将会触发Full GC回收整个堆内存。
- Perm区存放的主要是类的Class对象,如果一个类被频繁地加载,也可能会导致Perm区满,Perm区的垃圾回收也是Full GC触发的。