GC 垃圾回收

垃圾回收机制是由垃圾收集器Garbage Collection
GC来实现的,GC是后台的守护进程。它的特别之处是它是一个低优先级进程,但是可以根据内存的使用情况动态的调整他的优先级。因此,它是在内存中低到一定限度时才会自动运行,从而实现对内存的回收。这也是垃圾回收的时间不确定的原因

为何要这样设计:因为GC也是进程,也要消耗CPU等资源,如果GC执行过于频繁会对java的程序的执行产生较大的影响(java解释器本来就不快),因此JVM的设计者们选着了不定期的gc。

一、为什么要进行垃圾回收

我们知道Java是一门面向对象的语言,在一个系统运行中,会伴随着很多对象的创建,而这些对象一旦创建了就占据了一定的内存,在上一篇中,我们介绍过创建的对象是保存在堆中的,当对象使用完毕之后,不对其进行清理,那么会一直占据内存空间,很明显内存空间是有限的,如果不回收这些无用的对象占据的内存,那么新创建的对象申请不了内存空间,系统就会抛出异常而无法运行,所以必须要经常进行内存的回收,也就是垃圾收集。

二、回收

在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一 件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(即不可能再被任何途径 使用的对象)。

1、引用计数算法

引用计数是垃圾收集器中的早期策略。在这种方法中,堆中每个对象实例都有一个引用计数。当一个对象被创建时,就将该对象实例分配给一个变量,该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时,计数加1(a = b,则b引用的对象实例的计数器+1),但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1。任何引用计数器为0的对象实例可以被当作垃圾收集。当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1

优缺点

优点:引用计数收集器可以很快的执行,交织在程序运行中。对程序需要不被长时间打断的实时环境比较有利。

缺点:无法检测出循环引用。如父对象有一个对子对象的引用,子对象反过来引用父对象。这样,他们的引用计数永远不可能为0。

2、可达性分析算法

可达性分析算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点,无用的节点将会被判定为是可回收的对象。

img

在Java语言中,可作为GC Roots的对象包括下面几种:

a) 虚拟机栈中引用的对象(栈帧中的本地变量表);

b) 方法区中类静态属性引用的对象;

c) 方法区中常量引用的对象;

d) 本地方法栈中JNI(Native方法)引用的对象。

这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。如图所示,对象object 5、object 6、object 7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。

二、如何判断对象为垃圾对象

在JVM中主要的垃圾收集算法有:标记-清除、标记-清除-压缩(简称**“标记-整理”)、标记-复制-清除(简称“复制”、分代收集算法**。这几种收集算法互相配合,针对不同的内存区域采取对应的收集算法实现(这里具体是由相应的垃圾收集器实现)

垃圾回收涉及到大量的程序细节,而且各个平台的虚拟机操作内存的方式也不一样,但是他们进行垃圾回收的算法是通用的,所以这里我们也只介绍几种通用算法。

①、标记-清除算法

算法实现:分为标记-清除两个阶段,首先根据上面的根搜索算法标记出所有需要回收的对象,在标记完成后,然后在统一回收掉所有被标记的对象。

缺点

1、效率低:标记和清除这两个过程的效率都不高。

2、容易产生内存碎片:因为内存的申请通常不是连续的,那么清除一些对象后,那么就会产生大量不连续的内存碎片,而碎片太多时,当有个大对象需要分配内存时,便会造成没有足够的连续内存分配而提前触发垃圾回收,甚至直接抛出OutOfMemoryExecption。

img

②、复制算法

为了解决标记-清除算法的两个缺点,复制算法诞生了。

算法实现:将可用内存按容量划分为大小相等的两块区域,每次只使用其中一块,当这一块的内存用完了,就将还活着的对象复制到另一块区域上,然后再把已使用过的内存空间一次性清理掉。

优点:每次都是只对其中一块内存进行回收,不用考虑内存碎片的问题,而且分配内存时,只需要移动堆顶指针,按顺序进行分配即可,简单高效。

缺点:将内存分为两块,但是每次只能使用一块,也就是说,机器的一半内存是闲置的,这资源浪费有点严重。并且如果对象存活率较高,每次都需要复制大量的对象,效率也会变得很低。

img

③、标记-整理算法

上面我们说过复制算法会浪费一半的内存,并且对象存活率较高时,会有过多的复制操作,效率低下。

如果对象存活率很高,基本上不会进行垃圾回收时,标记-整理算法诞生了。

算法实现:首先标记出所有存活的对象,然后让所有存活对象向一端进行移动,最后直接清理到端边界以外的内存。

局限性:只有对象存活率很高的情况下,使用该算法才会效率较高。

img

④、分代收集算法

当前商业虚拟机都是采用此算法,但是其实这不是什么新的算法,而是上面几种算法的合集。

算法实现:根据对象的存活周期不同将内存分为几块,然后不同的区域采用不同的回收算法。

对于 HotSpot 虚拟机,它将堆空间分为老年代和新生代两块区域

1、对于存活周期较短,每次都有大批对象死亡,只有少量存活的区域,采用复制算法,因为只需要付出少量存活对象的复制成本即可完成收集;

2、对于存活周期较长,没有额外空间进行分配担保的区域,采用标记-整理算法,或者标记-清除算法。

堆有新生代和老年代两块区域组成,而新生代区域又分为三个部分,分别是 Eden,From Surivor,To Survivor ,比例是8:1:1。

新生代采用复制算法,每次使用一块Eden区和一块Survivor区,当进行垃圾回收时,将Eden和一块Survivor区域的所有存活对象复制到另一块Survivor区域,然后清理到刚存放对象的区域,依次循环。

老年代采用标记-清除或者标记-整理算法,根据使用的垃圾回收器来进行判断。

至于为什么要这样,这是由于内存分配的机制导致的,新生代存的基本上都是朝生夕死的对象,而老年代存放的都是存活率很高的对象。关于内存分配下篇博客我们会详细进行介绍。

四、何时进行垃圾回收

理清了什么是垃圾,怎么回收垃圾,最后一点就是Java虚拟机何时进行垃圾回收呢?

程序员可以调用 System.gc()方法,手动回收,但是调用此方法表示希望进行一次垃圾回收。但是它不能保证垃圾回收一定会进行,而且具体什么时候进行是取决于具体的虚拟机的,不同的虚拟机有不同的对策。

其次虚拟机会自行根据当前内存大小,判断何时进行垃圾回收,比如前面所说的,新生代满了,新产生的对象无法分配内存时,便会触发垃圾回收机制。

这里需要说明的是宣告一个对象死亡,至少要经历两次标记,前面我们说过,如果对象与GC Roots 不可达,那么此对象会被第一次标记并进行一次筛选,筛选的条件是此对象是否有必要执行 finalize() 方法,当对象没有覆盖 finalize()方法,或者该方法已经执行了一次,那么虚拟机都将视为没有必要执行finalize()方法。

如果这个对象有必要执行 finalize() 方法,那么该对象将会被放置在一个有虚拟机自动建立、低优先级,名为 F-Queue 队列中,GC会对F-Queue进行第二次标记,如果对象在finalize() 方法中成功拯救了自己(比如重新与GC Roots建立连接),那么第二次标记时,就会将该对象移除即将回收的集合,否则就会被回收。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/535625.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何让你变得魅力十足

我们每个人都希望自己在某些方面对他人来说是有用的。我们渴望那种被人需要的感觉,觉得自己是有能力的,就像我们在某方面很与众不同,很独特一样。 有些人非常有吸引力。他们是那些每当需要帮助便会被想起的人。他们是那些另你觉得非常有帮助…

日志linux

syslog日志系统: syslogd 系统,非内核产生的信息 man 2 syslog klogd 内核,专门负责内核产生的信息 man 3 syslog /var/log/messages 系统标准错误日志信息,非内核 syslogd /var/log/dmesg klogd 共同配置文件etc/…

sysbench的安装和做性能测试

sysbench是一个模块化的、跨平台、多线程基准测试工具,主要用于评估测试各种不同系统参数下的数据库负载情况。关于这个项目的详细介绍请看:http://sysbench.sourceforge.net。它主要包括以下几种方式的测试:1、cpu性能2、磁盘io性能3、调度程…

加密解密

PKI public key Infrastructure 公钥基础设施 CRl 证书吊销列表 CA证书颁发机构 Certificate Authority x509 证书 包括公钥、过期时间、证书的合法拥有者、证书如何被使用 CA的信息 CA的校验码等等 Pki实现方式 TLS/ssl:x509 opengpg ssl安全的套接字层…

高性能MySQL(1)——MYSQL架构

MySQL最重要、最与众不同的特性是它的存储引擎架构,这种架构将查询处理与数据的存储/提取相分离,使得可以在使用时根据不同的需求来选择数据存储的方式。 一、Mysql逻辑架构 如果能在头脑中构建出一幅MySQL各组件之间如何协同工作的架构图,就…

数据库设计中的14个关键技巧

1. 原始单据与实体之间的关系  可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。在特殊情况下,它们可能是一对多或多对一的关系,即一张原始单证对应多个实体&#xf…

高性能MySQL(2)——Schema与数据类型的优化

良好的逻辑设计和物理设计是高性能的基石,应该根据系统将要执行的查询语句来设计 schema,这往往需要权衡各种因素。 一、选择优化的数据类型 MySQL支持的数据类型非常多,选择正确的数据类型对于获得高性能至关重要。不管 存储哪种类型的数据&#xff0c…

用户权限sudo、suid、sgid以及facl等

su 切换用户或以指定用户运行命令。 使用su可以指定运行命令的身份(user/group/uid/gid)。 为了向后兼容,su默认不会改变当前目录,且仅设置HOME和SHELL这两个环境变量(若目标用户非root,则还设置USER和LOGNAME环境变量)。推荐使用--login选项…

MySQL 服务器调优

关于 MySQL 调优 有 3 种方法可以加快 MySQL 服务器的运行速度,效率从低到高依次为: 替换有问题的硬件。 对 MySQL 进程的设置进行调优。 对查询进行优化。 替换有问题的硬件通常是我们的第一考虑,主要原因是数据库会占用大量资源。不过这…

通过脚本启动批量服务

/app/all_start_script/wwyt/此目录服务如下:apigateway.sh auth.sh config.sh register.sh zipkin.sh /app/all_start_script/other/此目录服务如下: tomcat.sh wwyt_base.sh wwyt_cache.sh wwyt_flow.sh wwyt_risk_login.sh ww…

高性能MySQL(3)——创建高性能索引

索引对于良好的性能非常关键。尤其是当表中的数据量越来越大时,索引对性能的影响愈发重要。 一、索引的类型 在MySQL中,索引是在存储引擎层而不是服务器层实现的。所以没用统一的索引标准,不同存储引擎的索引工作方式并不相同。 1.1、B-Tre…

linux 调优系列

Linux系统内核:修改TCP/IP调优参数 所有的TCP/IP调优参数都位于/proc/sys/net/目录。例如, 下面是最重要的一些调优参数, 后面是它们的含义: 1. /proc/sys/net/core/rmem_max — 最大的TCP数据接收缓冲。 2. /proc/sys/net/core/wmem_max — 最大的TCP数据发送缓冲。 3.…

java中的构造方法与代码块

一、构造方法 1.1、java中的构造方法跟普通方法有很大的区别: 构造方法的方法名跟类名相同构造方法没有返回值类型,连void也没有,也不能用return返回值每次创建一个对象,都会调用构造方法,如果没有写构造方法,系统会默认加上一个空参的构造,如果已经写了构造方法,…

bash shell是如何识别特殊符号的

一 、 shell命令解析以及识别通配符 Shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令并把它送入内核去执行 。 实际上Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核。不仅如此,Shell有自己的编程语言用于对命令的编…

linux 调优系列(续)

linux 的各大发行版,都有些不必要的服务被默认开启了,针对ubuntu,我们 可以采用选择性关闭的方法加速起动,提高系统性能。 这里我们安装一个软件: sudo apt-get install sysv-rc-conf 然后这样起动: 在这个…

配置文件bashrc与profile的区别

1、当登入系统时候获得-个shell进程时,其读取环境设定档有三步 首先读入的是全局环境变量设定档/ete/profile,然后根据其内容读取额外的设定的文档,如/etc/profile. d和/ etc/ inputre 然后根据不同使用者帐号,去其家目录读取, bash, pr…

高性能MySQL(4)——查询性能优化

査询优化、索引优化、库表结构优化需要齐头并进,一个不落。 一、为什么查询速度为变慢 在尝试编写快速的查询之前,需要清楚一点,真正重要是响应时间。如果把查询看作是一个任务,那么他由一系列子任务组成,每个子任务都会消耗一定的时间。如果…

GooglePerformanceTools--tcmalloc

TCmalloc全称是Thread-Caching malloc,作者宣称tcmalloc相对于glibc2.3 malloc(aka ptmalloc2)有6倍的性能提高,tcmalloc的常用场景是用于加速MySQL,不过据Wikipedia的hacker Domas Mituzas说,tcmalloc不仅仅对MySQL起作用&#x…

linux基本命令以及命令常用选项

linux基本命令以及命令常用选项touch 创建文件,改变恩建时间戳,如果直接跟上一个文件,该文件不存在则创建文件-c文件不存在不创建文件,存在则改变文件的时间戳-a只改变文件的访问时间-m改变文件的修改时间-t时间格式CCYYMMDDhhmm…

Java获取上一周、上一个月、上一年的时间

SimpleDateFormat format new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”); Calendar c Calendar.getInstance(); 1.过去七天 c.setTime(new Date()); c.add(Calendar.DATE, - 7); Date d c.getTime(); String day format.format(d); System.out.println(“过去七天&#…