Java高级之虚拟机垃圾回收机制

简介: 博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved !区别于C语言手动回收,Java自动执行垃圾回收,但为了执行高效,需要了解其策略,更好的去应用。


博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved !


区别于C语言手动回收,Java自动执行垃圾回收,但为了执行高效,需要了解其策略,更好的去应用。

以下用HotSpot虚拟机为例,选取几个有意思的参数讲一下

1、默认GC时间为总时间的1%。也就是说GC线程设置有超时时间,防止卡死或过多妨碍主线程。

2、最高最低内存空闲比例分别为70%和40%。也就是说在小于70之后自动压缩,在大于40之后自动扩展。

3、最大内存为64M,达到即开始回收。

4、默认关闭来自System.gc()要求的垃圾回收。也就是说一般调用这个方法都没用。

5、默认Eden与Survivor的比例是8:1,Eden是主要存储区域,Survivor是次要的,配合老年代使用。

6、默认某对象经过15次GC后都没回收则进入老年代,默认老年代空间使用68%即被回收


Dalvik与JVM的最大差别在于,前者基于寄存器架构(句柄引用),后者基于栈架构(指针引用),也就是说前者处理速度更快。另外后者采用JIT的编译方式即时编译-也叫热加载,适用于J2EE;而前者采用AOT的加载方式,提前编译,虽然加载时间变长,但运行过程流畅,适用于J2ME,而IOS也是同样的原理。

新生代:一般是指大批对象产生的快,消亡的也快

老生代:一般是指大批对象产生后,不容易消亡


虚拟机底层回收算法有3种:

1、标记-清理(mark-sweep) 标记出所有需要回收的内存,然后统一清理;

2、复制算法(copying)上述5就是这种算法的逻辑,Eden和Survivor回收时,当剩余存活对象放到另一个Survivor里

3、标记-整理(mark-compact)标记出不需要回收的内存,复制到内存的一端,然后把另一端内存都回收掉

注:标记-清理、标记-整理均适用于老年代,复制算法适用于新生代。

特殊说明:

1、标记-清理劣势在于效率不高,因为要不断计算标记不活跃的对象;其次会产生很多碎片,不利于大数据对象的生成;

2、标记-整理适用于老年代,存活率高的话就特殊麻烦,而且如果内存是100%的存活就很麻烦,可能要几个虚拟机一起存储,这或许就是服务器集群的基础;

3、复制算法,也是对象存活率高的话,可能原来分配的Survivor区根本不够,也需要依赖其他内存块-或者直接将对象以持久化数据的形式存储在硬盘上,比起标记整理来说,内存利用率要高一些,因为标记整理差不多一半一半来分配空闲内存和活跃对象内存。

最后一种流行的垃圾回收机制,是分代收集算法,就是把前3种,按照新生代和老年代以及内存活跃度,采用相关比例及合适的回收机制来处理。以上就是垃圾回收机制的主要内容。


回收策略:由上述虚拟机最大内存和回收机制决定。其中引用计数也作为一种判断对象存活的算法,给对象添加一个引用计数器,有新地方引用就+1,不再引用就-1,为0时即可标记,稍后清除;但是,但是,但是,虚拟机并不是简单的使用这个算法来判断对象是否存活,对象引用、组合、聚合,最后循环引用,必然是行不通的,接下来了解更多。

GC Roots( 回收起点) 有以下几种

1、栈中引用的对象

2、方法区静态引用对象

3、方法区常用引用对象

4、JNI引用的对象

常见的由常量、方法区开始,搜索出使用过的路径,就叫引用链,当一个对象跟引用链没有连接,则标为可回收对象

同时该对象的finalize方法如果被调用过,或者未被覆盖(如果覆盖并被引用链引用,则会逃脱),则二次标记回收,正式进入回收队列F-Queue。而GC方法,仅是催促虚拟机加快回收,具体则由收集器的策略决定

以上两种第一种是人工代码层的回收策略,第二种是虚拟机的回收策略 


那么基于上述各种机制的垃圾回收器有几种呢,接下来继续看

做技术至今,发现一个问题:没有万能的方法,只有最合适的方法。垃圾回收器也是如此,没有万能的通用垃圾回收器,只有最合适的垃圾回收器,偶尔两个回收器也会搭手共同完成任务。

下面的回收器,依次优化升级,越来越高级!

Serial 最早的垃圾回收器,单线程操作,意思就是虚拟机启动1个小时得休息5分钟,用来做垃圾回收;现在也是处理器要分出一个来处理垃圾回收,不过变成多线程的;最初的产品设计,可以理解嘛,完成任务是第一要求,能用才能把事继续做下去。优点是,没有多线程交互的开销,只要不频繁,如果只有200M以内的新生代(注意老年代不行)内存,100毫秒以内可以回收。使用标记-清理算法(配第一种回收器),新生代收集器

ParNew,Serial的多线程版本,CPU越多,执行越高效,少的时候由于线程交互,效率未必比Serial高,但首次实现不等待操作(由于用户线程等级>垃圾回收线程)使用复制算法,新生代收集器

Parallel Scavenge 同ParNew一样,不同处在于它以吞吐量为先,运行代码时间/CPU总消耗时间,也就要尽可能要求更长的运行时间,减少GC的次数,这样用户的体验就会好一些。使用复制算法,新生代收集器。无法与CMS配合

Serial Old 单线程,使用标记-整理算法的老年代收集器-应用:1、与前一种回收器配合,2、作为CMS的后备方案

Parallel Old 多线程,使用标记-整理算法的老年代收集器

CMS,Concurrent Mark Sweep 使用标记-清理算法,以回收停顿时间最短为标准 ,因此体验较好,有并发标记、初始标记、重新标记、并发清除四个过程,初始和重新标记均会停下所有线程。

缺点有3

1、标记清除,容易使内存不连续,最后大对象很难生成; 

2、跟CPU关系很大,开启的线程数=(CPU数量+3)/4;

3、无法处理浮动内存,因为并发收集,所以收集期间仍然会产生新的垃圾。标记整理和标记清除的算法都适用于老年代收集器

G1 Garbage First 将整个堆(新生代和老年代)划分成多块,跟踪垃圾堆集的程度,根据允许收集的时候,首先收集垃圾最多的区域。这样既能保证吞吐量,又能减少卡顿时间(指定只能收集多长时间),使用标记整理算法,适用老年代垃圾收集器。

内存由新生代的Eden(对象优先存储在这里)+Survivor+老年代组成,内存不足先执行Minor GC,如果内存仍然不够,让老年代担保,Survivor对象进入老年代,否则进行Full GC

一般大对象(byte[]、年龄大于15(Minor一次岁数+1)以上的对象或者Survivor空间相同年龄的总和大于空间一半,则直接进入老年代

最后再介绍一下引用,强引用、软引用、弱引用、虚引用

一般情况下常量都是强引用,软引用的对象在内存不足时会被回收,弱引用执行一次Minor GC即被回收,虚引用不会导致对象被回收,只是为了在该对象被回收时得到一个通知。

垃圾回收器是虚拟机的重要组成部分,另一块就是内存分配,还有线程并发类等结构定义、文件加载等模块

常见虚拟机有三种:

 HotSpot(就是上面举例那种,Sun公司97年收购并作为默认虚拟机)

 JRockit(Bea公司02年收购,对服务器硬件和使用场景高度优化,即可编译,号称最快虚拟机)

J9(IBM独立开发,作为支持其硬件销售的虚拟机,可以说并不开放)

其他Sun Classic(虚拟机鼻祖)Sun Exact(与HotSpot共存过的虚拟机,后被并入HotSpot)

Apache Harmony(兼容1.5和1.6,催生安卓,由于TCK-技术兼容包问题,与Oracle决裂,退出JCP-Java委员会)意味着安卓是并非严格执行J2EE标准

嵌入虚拟机:

Dalvik,名字来源于冰岛一个渔村,执行ART的dex而非JIT的class文件,使得它的运行速度更快

KVM,简单、轻量、高移植性,Android和IOS出现以前,广泛应用于手机平台,但运行较慢

CDC/CLDC Sun设计用来代替上面的虚拟机

下次咱们讲讲Dalvik的原理

目录
相关文章
|
10天前
|
监控 算法 Java
深入理解Java的垃圾回收机制
【10月更文挑战第22天】在Java的世界里,有一个默默无闻却至关重要的角色——垃圾回收(Garbage Collection, GC)。就像城市的清洁工一样,它默默地清理着不再使用的内存空间,确保我们的程序运行得既高效又稳定。但你真的了解垃圾回收是如何工作的吗?让我们一起探索这个看似简单却充满奥秘的过程,看看它是如何影响你的Java应用性能的。
|
19天前
|
存储 监控 算法
Java中的内存管理与垃圾回收机制解析
本文深入探讨了Java编程语言中的内存管理方式,特别是垃圾回收机制。我们将了解Java的自动内存管理是如何工作的,它如何帮助开发者避免常见的内存泄漏问题。通过分析不同垃圾回收算法(如标记-清除、复制和标记-整理)以及JVM如何选择合适的垃圾回收策略,本文旨在帮助Java开发者更好地理解和优化应用程序的性能。
|
28天前
|
算法 Java 开发者
Java中的垃圾回收机制:从原理到实践
Java的垃圾回收机制(Garbage Collection, GC)是其语言设计中的一大亮点,它为开发者提供了自动内存管理的功能,大大减少了内存泄漏和指针错误等问题。本文将深入探讨Java GC的工作原理、不同垃圾收集器的种类及它们各自的优缺点,并结合实际案例展示如何调优Java应用的垃圾回收性能,旨在帮助读者更好地理解和有效利用Java的这一特性。
|
30天前
|
监控 算法 Java
Java中的内存管理:理解垃圾回收机制
【10月更文挑战第2天】 在本文中,我们将深入探讨Java编程语言中的内存管理机制,特别是垃圾回收机制。我们将从基本原理、垃圾回收算法到实际应用场景全面解析,帮助你更好地理解和优化Java应用的内存使用。无论你是初学者还是有经验的开发者,这篇文章都能带给你新的启发和思考。
31 2
|
22天前
|
存储 监控 算法
深入理解Java内存模型与垃圾回收机制
【10月更文挑战第10天】深入理解Java内存模型与垃圾回收机制
16 0
|
22天前
|
监控 算法 Java
Java中的垃圾回收机制深度解析
【10月更文挑战第10天】 本文深入探讨了Java语言核心特性之一的垃圾回收机制(Garbage Collection, GC),揭示了其在内存管理中的关键角色。通过对GC的工作原理、分类、算法以及调优策略的细致分析,旨在帮助开发者更好地理解并有效利用这一机制,提升Java应用的性能与可靠性。不同于常规摘要,本文聚焦于为读者提供一份关于Java GC全面而深入的解读,助力把握Java内存管理的精髓。
|
1月前
|
监控 算法 Java
Java中的内存管理:理解垃圾回收机制
本文深入探讨了Java编程语言中的内存管理,特别是其垃圾回收机制。我们将从基本原理出发,逐步解析垃圾回收的工作流程、优缺点以及如何通过编程实践优化应用性能。此外,文章还将讨论Java 11中引入的ZGC(Z Garbage Collector)这一新兴技术,帮助读者更好地理解和利用现代Java环境中的内存管理特性。
|
9天前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
81 38
|
7天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
1天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####