JVM内存管理机制和垃圾回收机制⼀个java程序的编译和执⾏过程如下:
根据 JVM 规范,JVM 内存共分为虚拟机⽅法区、堆、栈、程序计数器、本地⽅法栈五个部分。
the shawshank redemption1.Java与C语⾔相⽐的垃圾回收的⼀个优势?
Java与C语⾔相⽐的⼀个优势是,可以通过⾃⼰的JVM⾃动分配和回收内存空间。
垃圾回收机制是由垃圾收集器Garbage Collection来实现的,GC是后台⼀个低优先级的守护进程。
在内存中低到⼀定限度时才会⾃动运⾏,因此垃圾回收的时间是不确定的。
翌日的意思2.为何要这样设计(GC回收不确定时间,⾃动运⾏)?
因为GC也要消耗CPU等资源,如果GC执⾏过于频繁会对Java的程序的执⾏产⽣较⼤的影响,因此实⾏不定期的GC。
3.全部都通过GC⾃动回收吗?
不是,垃圾回收GC只能回收通过new关键字申请的内存(在堆上),但是堆上的内存并不完全是通过new申请分配的。
还有⼀些本地⽅法,这些内存如果不⼿动释放,就会导致内存泄露,所以需要在finalize中⽤本地⽅法(nativemethod)如free操作等,再使⽤gc⽅法。
4.何为垃圾?
对象之间的引⽤可以抽象成树形结构,通过树根(GC Roots)作为起点,当⼀个对象到GC Roots没有任何引⽤链相连时(不可达),则证明这个对象为可回收的对象。
有⼀下三种:
(1)栈帧中的本地变量表所引⽤的对象。
(2)⽅法区中类静态属性和常量引⽤的对象。
(3)本地⽅法栈中JNI(Native⽅法)引⽤的对象。
【垃圾产⽣的情况举例】
1.改变对象的引⽤,如置为null或者指向其他对象
eventhorizonObject obj1 = new Object();
Object obj2 = new Object();
obj1 = obj2; //obj1成为垃圾
obj1 = obj2 = null ; //obj2成为垃圾
2.引⽤类型:
强引⽤:是最难被GC回收的,宁可虚拟机抛出异常,中断程序,也不回收强引⽤指向的实例对象。
//强引⽤(指向实例对象,存在堆中)出现内存不够⽤OutOfMemoryError也不会回收
Object obj=new Object();
软引⽤ (SoftReference),在内存不⾜时,GC会回收软引⽤指向的对象(软引⽤可⽤来实现内存敏感的⾼速缓存。)
软引⽤可以和⼀个引⽤队列(ReferenceQueue)联合使⽤,如果软引⽤所引⽤的对象被垃圾回收,Java虚拟机就会把这个软引⽤加⼊到与之关联的引⽤队列中。
//软引⽤
undefined什么意思String str="hello yihaha";
SoftReference<String> soft=new SoftReference<String>(str);//将强引⽤转成软引⽤
System.out.());
弱引⽤(WeakReference),不管内存⾜不⾜,只要我GC,我都可能回收弱引⽤指向的对象(。不过由于垃圾回收器是⼀个优先级很低的线程,因此不⼀定会很快发现那些弱引⽤的对象。)
//弱引⽤
WeakReference<String> wReference=new WeakReference<String>(str);
System.out.());
3.循环每执⾏完⼀次,⽣成的Object对象都会成为可回收的对象
for(int i=0;i<10;i++) {
Object obj = new Object();
System.out.Class());
}
虚引⽤(PhantomReference ),虚引⽤必须和引⽤队列(ReferenceQueue)联合使⽤。
当垃圾回收器发现⼀个对象有虚引⽤时,⾸先执⾏所引⽤对象的finalize()⽅法,在回收内存之前,把这个虚引⽤对象加⼊到引⽤队列中, 你可以通过判断引⽤队列中是否有该虚引⽤对象,来了解这个对象是否将要被垃圾回收。
然后就可以利⽤虚引⽤机制完成对象回收前的⼀些⼯作。(注意:当JVM将虚引⽤插⼊到引⽤队列的时候,虚引⽤执⾏的对象内存还是存在的。但是PhantomReference并没有暴露API返回对象。
所以如果我想做清理⼯作,需要继承PhantomReference类,以便访问它指向的对象。)
//虚引⽤
ReferenceQueue<String> queue=new ReferenceQueue<>();
scrum
PhantomReference<String> phantomReference=new PhantomReference<String>(str,queue);
System.out.());
4.类嵌套
class A{
A a;
}
wipeA x = new A();//分配了⼀个空间
x.a = new A();//⼜分配了⼀个空间
x = null;//产⽣两个垃圾
5.线程中的垃圾
calss A implements Runnable{
void run(){
//....
}
}
//main
A x = new A();
x.start();minus
x=null; //线程执⾏完成后x对象才被认定为垃圾
5.如何⾼效地进⾏垃圾回收?
引⽤计数法
引⽤计数法是最经典的⼀种垃圾回收算法。其实现很简单,对于⼀个A对象,只要有任何⼀个对象引⽤了A,则A的引⽤计算器就加1,当引⽤失效时,引⽤计数器减1.只要A的引⽤计数器值为0,则对象A就不可能再被使⽤。
缺点:⽆法处理循环引⽤的问题,因此在Java的垃圾回收器中,没有使⽤该算法
引⽤计数器要求在每次因引⽤产⽣和消除的时候,需要伴随⼀个加法操作和减法操作,对系统性能会有⼀定的影响。
Mark-Sweep(标记-清除)算法
标记-清除算法分为两个阶段:标记阶段和清除阶段。
标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占⽤的空间。
标记-清除算法实现起来⽐较容易,但是有⼀个⽐较严重的问题就是容易产⽣内存碎⽚,碎⽚太多可能会导致后续过程中需要为⼤对象分配空间时⽆法找到⾜够的空间⽽提前触发GC。
Copying(复制)算法
Copying算法将可⽤内存按容量划分为⼤⼩相等的两块,每次只使⽤其中的⼀块。当这⼀块的内存⽤完了,就将还存活着的对象复制到另外⼀块上⾯,
然后再把第⼀块内存上的空间⼀次清理掉,这样就不容易出现内存碎⽚的问题,并且运⾏⾼效。
但是该算法导致能够使⽤的内存缩减到原来的⼀半。⽽且,该算法的效率跟存活对象的数⽬多少有很⼤的关系,如果存活对象很多,那么Copying算法的效率将会⼤⼤降低。
prepare的名词Mark-Compact(标记压缩)算法
为了解决Copying算法的缺陷,充分利⽤内存空间,提出了Mark-Compact算法。
该算法标记阶段标记出所有需要被回收的对象,但是在完成标记之后不是直接清理可回收对象,⽽是将存活的对象都移向⼀端,然后清理掉端边界以外的所有内存(只留下存活对象)。
Generational Collection(分代收集)算法
核⼼思想是将堆区划分为⽼年代(Tenured Generation)和新⽣代(Young Generation),⽼年代的
特点是每次垃圾收集时只有少量对象需要被回收,⽽新⽣代的特点是每次垃圾回收时都有⼤量的对象需要被回收,那么就可以在不同代的采取不同的最适合的收集算法。
新⽣代采⽤copying算法。但是不是划分成两块⼀样⼤⼩的空间,是将新⽣代划分为⼀块较⼤的Eden空间和两块较⼩的Survivor空间(from空间,和to空间)(⽐例8:1:1)每次使⽤Eden空间和Survivor from空间,当进⾏回收时,
将还存活的对象复制到Survivor to空间中,然后清理掉Eden和from空间。在进⾏了第⼀次GC之后,Eden区和from被清空,from和to 交换⾓⾊,下次GC时会将存活对象复制到to空间,如此反复循环。
新⽣代回收发⽣在新⽣代内存已经满了,或者说剩余内存⼩于即将new出来的对象的体积的时候。此时会发⽣新⽣代GC。
⽼年代采⽤标记压缩算法。它的优点是 实现简单⾼效,但是缺点是会给⽤户 带来停顿。
⽼年代回收发⽣在剩余内存⽆法装载新⽣代存活的对象的时候和⽆法装载⼤对象的时候(⼤对象直接进⼊⽼年代)。
⽅法区中有 永久代,它⽤来存储class⽂件、静态对象、⽅法描述等。对永久代的回收主要回收两部分内容:废弃常量和⽆⽤的类。
持久代⼤⼩通过-XX:MaxPermSize=<N>进⾏设置。
注意:当对象在Survivor区躲过⼀次GC的话,其对象年龄便会加1,默认情况下,对象年龄达到15时,(或者当 Survivor to空间满了)就会移动到⽼年代中。⼀般来说,⼤对象会被直接分配到⽼年代,所谓的⼤对象是指需要⼤量连续存储空间的对象,最常见的⼀种⼤对象就是⼤数组,⽐如:byte[] data = newbyte[4*1024*1024]。
分区算法
lead to算法思想:分区算法将整个堆空间划分为连续的不同⼩区间,每⼀个⼩区间都独⽴使⽤,独⽴回收。
算法优点是:可以控制⼀次回收多少个⼩区间
通常,相同的条件下,堆空间越⼤,⼀次GC所需的时间就越长,从⽽产⽣的停顿时间就越长。为了更好的控制GC产⽣的停顿时间,将⼀块⼤的内存区域分割成多个⼩块,根据⽬标的停顿时间,每次合理的回收若⼲个⼩区间,⽽不是整个堆空间,从⽽ 减少⼀个GC的停顿时间。
6.关于收集器介绍?
对于新⽣代Copying算法,
Seria l收集器是 单线程,并且在它进⾏垃圾收集时,必须 暂停所有⽤户线程。2013年普通高等学校招生全国统一考试
ParNew收集器是Serial收集器的 多线程版本,在单CPU甚⾄两个CPU的环境下,由于线程交互的开销,⽆法保证性能超越 Serial收集器。
Parallel Scavenge 新⽣代的多线程收集器( 并⾏收集器),它主要是为了达到⼀个 可控的吞吐量
对于⽼年代使⽤Mark-Compact算法,
Serial Old: 单线程 ,Serial收集器的⽼年代版本。
Parallel Old多线程可控吞吐量(Parallel Scavenge收集器的⽼年代版本)
CMS(Current Mark Sweep)收集器采⽤的是 标记-清除算法, 内存碎⽚问题不可避免。是⼀种 并发低停顿收集器。(可以 使⽤-XX:CMSFullGCsBeforeCompaction
设置执⾏⼏次CMS回收后,跟着来⼀次内存碎⽚整理。)
G1收集器是当今收集器技术发展最前沿的成果,它是⼀款⾯向服务端应⽤的收集器,充分利⽤多CPU、多核环境。因此它是⼀款 并⾏与并发收集器,并且它能建⽴ 可预测的停顿时间模型。
7.关于GC种类?
GC其实准确分类只有两⼤种:
Partial GC:并不收集整个GC堆的模式
Young GC(Minor GC):只收集young gen的GC(当young gen中的eden区分配满的时候触发。)
Old GC( Major GC ):只收集old gen的GC。只有CMS的concurrent collection是这个模式
Mixed GC:收集整个young gen以及部分old gen的GC。只有G1有这个模式
Full GC:收集整个堆,包括young gen、old gen、perm gen(如果存在的话)等所有部分的模式。
(young GC准备触发时,统计出young GC中要保存到年⽼代中的数据⼤⼩⼤于年⽼代剩余的存储空间时,或者要在持久代中分配空间但是没有⾜够空间时,便触发full GC)
blog.csdn/u010429424/article/details/77333311