Java虚拟机
0. 对哪些区域回收
Java运行时数据区域:程序计数器、JVM栈、本地方法栈、方法区和堆。
由于程序计数器、JVM栈、本地方法栈3个区域随线程而生随线程而灭,对这几个区域内存的回收和分配具有确定性。而方法区和堆则不一样,程序需要在运行时才知道创建哪些对象,对这部分内存的分配是动态的,GC关注的也就是这部分内存。
1. 主动GC
调用system.gc() Runtime.getRuntime.gc()
2. 垃圾回收
释放那些不在持有任何引用的对象的内存
3. 怎样判断是否需要收集
- 引用计数法:对象没有任何引用与之关联(无法解决循环引用)
ext:Python使用引用计数法
- 可达性分析法:通过一组称为GC Root的对象为起点,从这些节点向下搜索,如果某对象不能从这些根对象的一个(至少一个)所到达,则判定该对象应当回收。
ext:可作为GCRoot的对象:虚拟机栈中引用的对象。方法区中类静态属性引用的对象,方法区中类常量引用的对象,本地方法栈中JNI引用的对象
4.对象的自我救赎
即使在可达性算法中判定为不可达时,也并非一定被回收。对象存在自我救赎的可能。要真正宣告对象的死亡,需要经历2次标记的过程。如果对象经过可达性分析法发现不可达时,对象将被第一次标记被进行筛选,筛选的条件是此对象是否有必要执行finalize方法。如果对象没有重写finalize方法或finalize方法已经被JVM调用过,则判定为不需要执行。
如果对象被判定为需要执行finalize方法,该对象将被放置在一个叫做F-Queue的队列中,JVM会建立一个低优先级的线程执行finalize方法,如果对象想要完成自我救赎需要在finalize方法中与引用链上的对象关联,比如把自己也就是this赋值给某个类变量。当GC第二次对F-Queue中对象标记时,该对象将被移出“即将回收”的集合,完成自我救赎。简言之,finalize方法是对象逃脱死亡命运的最后机会,并且任何对象的finalize方法只会被JVM调用一次。
5.垃圾回收算法
Mark-Sweep法:标记清除法 容易产生内存碎片,导致分配较大对象时没有足够的连续内存空间而提前出发GC。这里涉及到另一个问题,即对象创建时的内存分配,对象创建内存分配主要有2种方法,分别是指针碰撞法和空闲列表法。指针碰撞法:使用的内存在一侧,空闲的在另一侧,中间使用一个指针作为分界点指示器,对象内存分配时只要指针向空闲的移动对象大小的距离即可。 空闲列表法:使用的和空闲的内存相互交错无法进行指针碰撞,JVM必须维护一个列表记录哪些内存块可用,分配时从列表中找出一个足够的分配给对象,并更新列表记录。所以,当采用Mark-Sweep算法的垃圾回收器时,内存分配通常采用空闲列表法。
Copy法:将内存分为2块,每次使用其中的一块,当一块满了,将存活的对象复制到另一块,把使用过的那一块一次性清除。显然,Copy法解决了内存碎片的问题,但算法的代价是内存缩小为原来的一半。现代的垃圾收集器对新生代采用的正是Copy算法。但通常不执行1:1的策略,HotSpot虚拟机默认Eden区Survivor区8:1。每次使用Eden和其中一块Survivor区。也就是说新生代可用内存为新生代内存空间的90%。
Mark-Compact法:标记整理法。它的第一阶段与Mark-Sweep法一样,但不直接清除,而是将存活对象向一端移动,然后清除端边界以外的内存,这样也不存在内存碎片。
分代收集算法:将堆内存划分为新生代,老年代,根据新生代老年代的特点选取不同的收集算法。因为新生代对象大多朝生夕死,而老年代对象存活率高,没有额外空间进行分配担保,通常对新生代执行复制算法,老年代执行Mark-Sweep算法或Mark-Compact算法。
7. 不同垃圾回收算法对比
- 标记清除法(Mark-Sweeping):易产生内存碎片
- 复制回收法(Copying):为了解决Mark-Sweep法而提出,内存空间减至一半
- 标记压缩法(Mark-Compact):为了解决Copying法的缺陷,标记后移动到一端再清除
- 分代回收法(GenerationalCollection):新生代对象存活周期短,需要大量回收对象,需要复制的少,执行copy算法;老年代对象存活周期相对长,回收少量对象,执行mark-compact算法.新生代划分:较大的eden区 和 2个survivor区
8. 内存分配
新生代的三部分 |Eden Space|From Space|To Space|,对象主要分配在新生代的Eden区
大对象直接进入老年代 大对象比如大数组直接进入老年代,可通过虚拟机参数-XX:PretenureSizeThreshold参数设置
长期存活的对象进入老年代 ext:虚拟机为每个对象定义一个年龄计数器,如果对象在Eden区出生并经过一次MinorGC仍然存活,将其移入Survivor的To区,GC完成标记互换后,相当于存活的对象进入From区,对象年龄加1,当增加到默认15岁时,晋升老年代。可通过-XX:MaxTenuringThreshold设置
GC的过程:GC开始前,对象只存在于Eden区和From区,To区逻辑上始终为空。对象分配在Eden区,Eden区空间不足,发起MinorGC,将Eden区所有存活的对象复制到To区,From区存活的对象根据年龄判断去向,若到达年龄阈值移入老年代,否则也移入To区,GC完成后Eden区和From区被清空,From区和To区标记互换。对象每在Survivor区躲过一次MinorGC年龄加一。MinorGC将重复这样的过程,直到To区被填满,To区满了以后,将把所有对象移入老年代。
动态对象年龄判定 suvivor区相同年龄对象总和大于suvivor区空间的一半,年龄大于等于该值的对象直接进入老年代
空间分配担保 在MinorGC开始前,虚拟机检查老年代最大可用连续空间是否大于新生代所有对象总空间,如果成立,MinorGC可以确保是安全的。否则,虚拟机会查看HandlePromotionFailure设置值是否允许担保失败,如果允许,继续查看老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于则尝试MinorGC,尽管这次MinorGC是有风险的。如果小于,或者HandlerPromotionFailure设置不允许,则要改为FullGC。
新生代的回收称为MinorGC,对老年代的回收成为MajorGC又名FullGC
10. 方法区的回收
方法区通常会与永久代划等号,实际上二者并不等价,只不过是HotSpot虚拟机设计者用永久代实现方法区,并将GC分代扩展至方法区。 永久代垃圾回收通常包括两部分内容:废弃常量和无用的类。常量的回收与堆区对象的回收类似,当没有其他地方引用该字面量时,如果有必要,将被清理出常量池。
判定无用的类的3个条件:
1.该类的所有实例都已经被回收,也就是说堆中不存在该类的任何实例
2.加载该类的ClassLoader已经被回收
3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
当然,这也仅仅是判定,不代表立即卸载该类。
64 位 JVM 中,int 的长度是多大?
Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位。意思就是说,在 32 位 和 64 位 的Java 虚拟机中,int 类型的长度是相同的。
Java 中 WeakReference 与 SoftReference的区别?
Java中一共有四种类型的引用。StrongReference、 SoftReference、 WeakReference 以及 PhantomReference。
StrongReference:Java 的默认引用实现, 它会尽可能长时间的存活于 JVM 内,当没有任何对象指向它时将会被GC回收
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
SoftReference:尽可能长时间保留引用,直到JVM内存不足,适合某些缓存应用
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。
软引用可用来实现内存敏感的高速缓存。
WeakReference:顾名思义, 是一个弱引用, 当所引用的对象在 JVM 内不再有强引用时, 下一次将被GC回收
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,
不管当前内存空间足够与否,都会回收它的内存
。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
PhantomReference:它是最弱的一种引用关系,也无法通过PhantomReference取得对象的实例。仅用来当该对象被回收时收到一个通知
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
虽然 WeakReference 与 SoftReference 都有利于提高 GC 和 内存的效率,但是 WeakReference ,一旦失去最后一个强引用,就会被 GC 回收,而 SoftReference 会尽可能长的保留引用直到 JVM 内存不足时才会被回收(虚拟机保证), 这一特性使得 SoftReference 非常适合缓存应用。
http://blog.csdn.net/mazhimazh/article/details/19752475
JRE、JDK、JVM 及 JIT 之间有什么不同?
JRE 代表 Java 运行时(Java run-time),是运行 Java 应用所必须的。JDK 代表 Java 开发工具(Java development kit),是 Java 程序的开发工具,如 Java 编译器,它也包含 JRE。JVM 代表 Java 虚拟机(Java virtual machine),它的责任是运行 Java 应用。JIT 代表即时编译(Just In Time compilation),当代码执行的次数超过一定的阈值时,会将 Java 字节码转换为本地代码,如,主要的热点代码会被准换为本地代码,这样有利大幅度提高 Java 应用的性能。
你能保证 GC 执行吗?
不能,虽然你可以调用 System.gc() 或者 Runtime.getRuntime().gc(),但是没有办法保证 GC 的执行。
Java中有内存泄漏吗?
内存泄露的定义: 当某些对象不再被应用程序所使用,但是由于仍然被引用而导致垃圾收集器不能释放。
内存泄漏的原因:对象的生命周期不同。比如说对象A引用了对象B. A的生命周期比B的要长得多,当对象B在应用程序中不会再被使用以后, 对象 A 仍然持有着B的引用. (根据虚拟机规范)在这种情况下GC不能将B从内存中释放。这种情况很可能会引起内存问题,倘若A还持有着其他对象的引用,那么这些被引用的(无用)对象也不会被回收,并占用着内存空间。甚至有可能B也持有一大堆其他对象的引用。这些对象由于被B所引用,也不会被垃圾收集器所回收,所有这些无用的对象将消耗大量宝贵的内存空间。并可能导致内存泄漏。
怎样防止:
1、当心集合类, 比如HashMap, ArrayList等,因为这是最容易发生内存泄露的地方.当集合对象被声明为static时,他们的生命周期一般和整个应用程序一样长。