GC
gc:java的垃圾回收机制
在java中,内存的分配是由程序完成的,而内存的释放是由GC完成
调用System类的静态方法gc()可以进行垃圾回收,但它只是向JVM发出一个申请,到底是否真正执行垃圾收集,一切都是个未知数
垃圾回收的三种算法
标记清除算法:
- 标记阶段:先通过根节点,标记所有从根节点开始的对象,未标记的未垃圾对象
- 清除阶段:清除所有未标记的对象
缺点:标记和清除的效率都不高
清除之后会产生大量不连续的内存碎片,不连续的内存空间使用效率比较低
复制算法:(新生代使用的算法)
- 将原有的内存空间分为两块,每次只使用其中的一块,在垃圾回收时,将正在使用的内存块中的存活的对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象
标记整理算法:(老年代使用的算法)
- 标记阶段:先通过根节点,标记所有从根节点开始的可达对象,未被标记的为垃圾对象
- 整理阶段:将所有存活的对象压缩到内存的一段,之后清理边界所有的空间
此外还有个古老的算法:引用计算法,其核心是对象在被引用时引用计数+1,而当引用不再使用时引用计数-1,当一个对象的引用计数为0时,则说明它将来不再被引用,因此可以释放对应的内存空间
三种算法比较
效率:复制算法>标记整理>标记清除(只是简单比较时间复杂度,实际情况不一定如此)
内存整齐度:复制算法=标记整理>标记清除
- 内存利用率:标记整理=标记清除>复制算法
GC引用可达性分析算法中,可作为GC Roots的对象
- java虚拟机栈中的对象
- 方法区中的静态成员
- 方法区中的常量引用对象
- 本地方法区中的JNI(Native方法)引用对象
GC的分代回收
新生代:(复制算法 GC:minor gc)
在方法中new一个对象,在这个方法调用完毕后,对象就会被回收,这就是一个典型的新生代对象。
新生代存放生命周期很短的对象,这部分对象在GC的时候很多已经是非活动对象,因此采用复制算法,只需将少量的存活对象copy到to space,存活越少,效率越高
新生代又分为:
- eden:每当对象创建的时候,总陪分配到这个区域
- survivor1:复制算法中的from space
- survivor2:复制算法中的to space
Hotspot默认将Eden区和两个survivor区按照8:2的比例划分
老年代:(标记整理算法 GC:major gc也叫full gc)
生命周期长,在新生代中经过多次minor gc依旧存活的对象。survivor空间不够用时,需要依赖老年代进行分配担保,所有大对象直接进入老年代。老年代区域比较大,需要时间长
永久代:(即方法区)
存放class信息,方法信息,常量池,静态变量
也会发生gc,这里的gc主要是对常量池的回收和对类的卸载
minor gc 与full gc
- minor gc是新生代的垃圾回收机制,复制算法,调用频繁,回收速度快
- full gc是老年代的垃圾回收机制,标记整理算法,调用不频繁,回收速度慢
- full gc发生的次数不会有minor gc那么频繁,但Time(full gc)>Time(minor gc)
- 当eden满了,就minor gc
- 当Eden+from space空间大于老年代剩余空间时,就会full gc
垃圾收集器
- Serial收集器:单线程独占
- 单线程收集器,不是只能使用一个CPU。在进行垃圾收集时,必须暂停其他所有的工作线程,直到收集结束
- 新生代–复制算法,老年代–标记整理算法
- 简单高效,client模式下默认的新生代收集器
- ParNew收集器:多线程独占
- ParNew收集器是Serial收集器的多线程版本
- 新生代–复制算法,老年代–标记整理算法
- server模式下首选新生代收集器
- ParNew Scanvenge收集器:
- 类似ParNew,但更加关注吞吐量
- G1收集器:
- 当今收集器发展的最前沿成果之一。对垃圾回收进行了划分优先级的操作,优先级的回收方式保证了他的高效率
- 最大的优点是结合了空间整合,不会产生大量的碎片,也降低了进行gc的频率
- CMS收集器: 多线程非独占
- 使用的是标记清除算法
- 一种以获得最短回收停顿时间为目标的收集器,适用于互联网站和B/S系统的服务器上
java中的内存泄漏
在Java中,内存泄漏就是存在一些被分配的对象,这些对象不会被GC回收,但它们却占用内存
这些对象有两个特点:
- 对象是可达的
对象是无用的
- 例子:循环申请Object对象并放入一个Vector中。如果仅仅释放引用本身,那么Vector仍然引用该对象,这个对象对于GC来说是不可回收的。如果对象加入到Vector后还要将其删除,最简单的办法就是将Vector对象置为null
Vector v = new Vector();
for (int i = 0; i < 100; i++) {
Object o = new Object();
v.add(o);
o = null;
}
v = null;
物理连接:例如数据库和网络连接,除非显示的关闭了连接,否则不会自动被GC回收。数据库的Connection这些连接是独立于JVM的,对于ResultSet 和Statement对象可以不进行显示回收,但Connection一定要显示回收,因为Connection在任何时候都无法自动回收,而Connection一旦回收,ResultSet和Statement就会立即为null。但是如果使用的是连接池的情况就不一样,处了要显示关闭连接,还要显示关闭ResultSet和Statement对象
单例模式:因为单例对象初始化后将在JVM的整个生命周期内存在,如果它持有一个外部对象的引用,那么这个外部对象就不能被回收,从而导致内存泄漏。如果这个外部对象还持有其他对象的引用,那么泄露更严重