一、内存区域:
方法区:存储已被虚拟机加载的类,常量,静态变量等。
堆区:存储对象的实例,是GC管理的主要区域。
虚拟机栈:用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
本地方法栈:与虚拟机栈一样,只不过为native层服务。
程序计数器:当前线程所执行的字节码的行号指示器,此取悦不会出现内存溢出。
二、内存分配:
新生代:新生代又分为Eden和Survivor区,对象优先分配在Eden区。
老年代:分配大对象,长期存活的对象。
三、GC:
Full GC,Minor GC, Major GC
Minor GC:清理新生代,当Eden区没有足够的空间进行分配时,虚拟机触发Minor GC。
Major GC:清理老年代,经常会伴随至少一次的Minor GC。
Full GC:清理新生代和老年代。
Full GC是清除堆空间的,包括新生代和老年代。
Full GC产生的条件:
老年代空间不足。
调用System.gc时,系统建议执行full GC,但是不必然执行。
通过Minor GC后进入老年代的平均大小> 老年代的可用内存。
分代收集算法:如果Eden区满了执行Minor GC后,对象依然存活,
将会被移到Survivor区,同时对象年龄设置为1.如果在Survivor区执行Major GC后依然存活,对象再加1,直到达到年龄阀值,被移到老年区。
四、垃圾收集器算法:
1.标记-清除: 作用于老年代
分两阶段:
首先是标记阶段,通过可达性分析标记出所有需要回收的对象;
其次是清除阶段,统一回收所有被标记的对象。
优点简单,但存在效率问题和内存碎片问题,即标记和清除过程的效率不高,且清除结束后会造成大量的碎片空间,可能导致在申请大块内存时因为没有足够的连续空间而再次触发垃圾收集。
2.标记-整理:作用于老年代
分两阶段:
首先是标记阶段,通过可达性分析标记出所有需要回收的对象,
然后在整理阶段将存活的对象迁移到一端,直接进行内存空间的整合。
这种算法适用于对象存活率较低的情况,提高了内存的使用效率。
3.复制-清除:作用于新生代
存活是复制,清除
该算法将内存划分为两块相等的区域,
每次使用其中一块,当内存不足时,将存活的对象复制到另一块上,然后清理掉是使用过的区域,这种算法解决了内存碎片问题,但牺牲了内存利用率,因为每次申请内存时只能使用一半的空间。通过分化为Eden区和两个Survivor区,提高了内存的使用效率。
4.分代收集:用对象年龄计数器把堆区分成新生代和老年代
这是一种综合了上诉几种算法的策略,根据对象存活的生命周期将内存划分为不同的区域。
新生代采用复制算法,老年代则采用标记-整理算法。
判断哪些是回收对象:
1.引用计数器(目前已废弃)
对象的引用类型:
强引用:只要引用存在,垃圾回收器不会回收。
软引用:在出现内存溢出之前,将会把这些对象回收。
弱引用:垃圾回收器工作时,无论当前内存是否足够,都会回收。
虚引用:为对象设置虚引用是能在对象回收时收到通知。
2.可达性分析算法:当一个对象到GC Roots没有任何引用链时,证明对象不可用。
五、GC Root:
虚拟机栈(栈帧中的本地变量表)中引用的对象
public calss Test{
public static void main(String[] args){
Test a = new Test();
a = null;
}
}
a是栈帧中的本地变量,当a = null时,由于此时a充当了GC Root的作用,a与原来指向的实例new Test()断开了链接,所以对象会被回收。
本地方法栈中JNI(即一般说的是Native方法)引用的对象,本地方法就是一个java调用非java代码的接口,该方法并非Java实现的。
方法区中类静态属性引用的对象:类的静态属性引用的对象。
public class Test{
public static Test s;
public static void main(String[] args){
Test a = new Test();
a.s = new Test();
a = null;
}
}
方法区中的常量引用的对象:常量s指向的对象并不会因为a指向的对象被回收而回收。
public class Test{
public static final Test s = new Test();
public static void main(String[] args){
Test a = new Test();
a = null;
}
}
GC Root (两栈两方法)