JVM内存区域划分
首先我们先看看他会被划分为那几个区域
1.堆(存放对象)
2.方法区(存放一些静态的东西和一些修饰代码之类的)
3.栈(局部的一些变量)
4.程序计数器(存放的是下一条指令的地址)
GC(垃圾回收)
首先我们要明确为什么要垃圾回收?
如果不垃圾回收,创建的对象越来越多,相对而言,能够分配的内训就会越来越少。这样服务器就可能宕机。这种我们称它为内存泄漏。
垃圾回收的两种手段
1.程序计数器(java中没有用到此方法)
比如有一个对象,创建该对象的同时,给这个对象的搭配一个程序计数器,当有引用指向这个对象,计数器就+1,每次有一个引用销毁,计数器就减一,当计数器为0的时候,这个对象就表示没用了。就会被回收。
例如:
void func{
Test t1 = new Test();
Test t2 = new Test();
}
当着方法执行完的话,这两个引用就没有指针指向它了,就会被回收。
但是这种方式是有很大的缺陷的:
1.因为涉及到++ 和 – 操作,就必须考虑多线程的情况,所以就必须要加锁,加锁了之后,程序的执行效率就会大大降低。
2.当有两个引用相互指向的时候,就无法回收,这种就叫做“循环引用问题”。
例如:
static class T{
public T t = null;
}
public static void main(String[] args){
T t1 = new Test();
T t2 = new Test();
t1.t = t2;
t2.t = t1;
}
2.可达性分析
java里的对象都是通过引用来获取到的,一个引用能指向一个对象,一个对象里可能还包含若干个引用,这个引用关系,就构成了图状结构。
例如:这样
若果这是a的应用不指向b了,就会变成这样:
此时b以及b下面的节点就会被认为是垃圾节点进行回收。
可达性分析总结一句话就是:从GCRoot出发,能够访问到的对象就是可用的,访问不到的就是“垃圾”。
java中的四种引用
1.强引用:(就是平时用的):能找到对象,也能决定对象的生死。
2.软引用:能找到对象,只能一定程度的决定对象的生死(延缓对象被回收的时机)
3.弱引用:能找到对象,不能决定对象的生死。
4.虚引用:不能找到对象,也不能决定对象的生死(只是一些特殊情况,比如进行一些善后工作)
垃圾回收机制的四种算法
一.标记清除
标记的方式就是通过刚才说的可达性分析,可达的对象刨除,剩下的就是被标记的垃圾对象。
如图,假如深色的就是被标记的,他被标记清除之后,就在内存中以内存碎片的形式存在,这个也是它的缺点。例如:每个格子都是1M内存,我现在new 了一个对象要申请2M大小的内存,是申请不到的,因为他们都是以1M的内存随拍你存在的~~
二.复制算法
如图,标记算法就是将内存分为两份,将为标记的对象拷贝到另一块内存中,然后将此内存中将当前对象标记,并把这块标记的内存进行回收。
优点:很好的解决了内存碎片问题。
缺点:1.有局限性,如果留下的对象多,可能会低效。
2. 内存利用率不高,要切出一半来。
三.标记整理
采取类似于“顺序表”删除中间元素的方式,搬运~
优点:没有内存碎片了,空间利用率也高了不少。
缺点:内存搬运操作比较频繁,效率不高。
四.分代回收
1.有一个新对象诞生于伊甸区。(新对象的内存在伊甸区上分配)
2.有一个经验规律表明,在伊甸区的新对象,绝大部分活不过一轮GC,熬过一轮GC进入幸存区。
3.幸存区的对象也会经受GC的考验,两个幸存区之间相互配合,使用复制算法,把每次经过GC考验的对象拷贝到另一个幸存区
4.经历重重GC之后,年龄累积到一定程度,晋升为老年代对象,把这个对象存=从幸存区拷贝到老年代
5.老年代的对象也不是不回收,而是大大降低了GC频率。
还有一个特例:如果是一个特别大的对象,就不适合在新生代复制来复制去的,直接进入老年代。
给大家介绍几个相关术语:
Partical GC : 进行部分内存区域的垃圾回收
Full GC : 针对全部内存进行回收
Minor GC: 进行大部分的内存回收(一般是指新生代)
Major GC : 进行大部分的内存回收 (一般指的是新生代和老年代)