GC基本原理学习(Garbage Collected)

引言

Java的内存动态分配和垃圾收集的问题,都交给了JVM来处理。注意,将JVM运行数据区(虚拟机栈【栈帧】,程序计数器,堆内存)粗略的分为栈和堆(所有线程共享),回收的是堆中的对象实例。不是栈中的引用类型。

那么JVM是如何处理的?

从三个问题来分析:

1. 哪些内存需要回收?

2. 什么时候进行回收?

3. 如何回收?

注:现代收集器基本采用分代收集算法,堆分为:新生代和老年代。

1. 哪些内存需要回收?什么时候回收?

1.1 了解下对象的创建:

  1. 通过new 关键字。
  2. JVM遇到new指令,检查是否能在常量池中定位到一个类的符号引用。
  3. 检查是否已被加载,解析,初始化过。
  4. 没有,则执行相应的类加载过程。
  5. 类加载检查通过后,为新生对象分配内存。(类加载后确定对象所需内存大小)
  6. 从Java堆中划分出一块确定的内存。

1.2 若为“死亡”的对象,则需要回收,如何判断对象是否存活?

1.2.1 引用计数算法

  • 给对象添加一个引用计数器
  • 每当有一个地方引用它时,计数器指加1
  • 当引用失效时,计数器指减1
  • 任何时刻计数器为0的对象就是需要回收的

    Java虚拟机没有采用这种方法来管理内存,主要原因是它难以解决对象之间相互循环引用的问题。(python中采用)

1.2.2 可达性分析算法

  • 用一个“GC Roots”的对象(指一系列中的其中一个并非某一种)作为起始点
  • 从该节点向下搜索,搜索走过的路径称为引用链
  • 若一个对象没有与任何引用链相连,即不可达
  • 证明该对象是不可用,死亡

    Java,C#采用此方法。

1.2.3 哪些可作为GC Roots的对象?

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI引用的对象

1.2.4 无论哪种算法都是与“引用”有关,下面分析引用的4种类型,强度依次减弱。

  • 强引用:Java中普遍存在的(如:Object obj = new Object()),只要强引用还在,垃圾收集器永远不会回收掉被引用的对象实例。
  • 软引用:用于有用但非必需的对象。在系统要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。[SoftReference]
  • 弱引用:用于非必需对象。被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。[WeakReference]
  • 虚引用:不影响生存时间,唯一目的就是能在这个对象被收集器回收时收到一个系统通知。[PhantomReference]

1.2.5 详解,真正的”死亡”对象(两次标记后进行回收

事实上,不可达的对象,也并非“非死不可”,这时它们处于“缓刑”阶段,真正死亡至少要经历两次标记过程:

1.2.5.1 第一次标记
  • 对对象进行可达性分析后发现没有与GC Roots相连接的引用链
  • 筛选是否有必要执行finalize()方法[没有覆盖finalize()方法或finalize()方法已经被JVM调用过,视为“无需执行”]
1.2.5.2 第二次标记【需要执行finalize(),通过筛选】
  • 将该对象放置F-Queue的队列中
  • JVM自动建立一个低优先级的Finalizer线程去执行它(会触发执行,但不一定等待它运行结束)
  • GC对F-Queue中的对象进行标记(对象可通过finalize()拯救自己,重新关联引用链等)

    不建议使用finalize()

1.3 上面讲的是需要回收的堆内存,关于回收方法区(或HotSpot虚拟机中的永久代),效率低

1.3.1 废弃常量

  • 与堆类似,没有任何对象和其它地方引用的常量
  • 随内存回收,被系统清理出常量池

1.3.2 无用的类

  • 该类所有的实例都已经被回收(堆中无该类的实例)
  • 加载该类的ClassLoader已经被回收
  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法通过反射访问该类的方法。

2. 如何回收?【垃圾收集算法】

2.1 标记-清除[Mark-Sweep]算法

  • 标记出所有需要回收的对象
  • 统一回收(清除)所有被标记的对象

    缺点:效率不高;标记清除后会产生大量不连续的内存碎片。

2.2 复制[Copying]算法

  • 将可用内存按容量划分为大小相等的两块
  • 每次只使用其中的一块
  • 当使用的这块内存用完了,则将还存活的对象复制到另一块上面
  • 把使用过的那块内存一次性清理掉

    优点:实现简单,运行高效,可按顺序分配内存。

    缺点:内存直接缩小为原来一半,代价太大;对象存活率较高时,效率变低。

2.3 标记-整理[Mark-Compact]算法

  • 标记出所有需要回收的对象
  • 让所有存活的对象都向一端移动(整理)
  • 清理掉存活对象端以外的内存

    适合堆中的老年代的垃圾收集

2.4 分代收集[Generational Collection]算法(商业虚拟机主要采用方法)

  • 根据对象存活周期的不同将内存划分为几块
  • 一般分为新生代和老年代
  • 不同年代采用最适当的收集算法
  • 新生代,一般复制算法
  • 老年代,一般“标记-清理”或“标记-整理”

    具体如何回收,需依据具体的垃圾收集器实现

关于内存泄露

  1. OutOfMemoryError异常,java堆溢出:-Xms ,-Xmx

    对象不断被创建,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常。

  2. VM Stack溢出。-Xss
  3. 常量池溢出。-XX:PermSize,-XX:MaxPermSize

参考:《深入理解Java虚拟机》

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-30 15:33:56

GC基本原理学习(Garbage Collected)的相关文章

最小错误率训练(mert)基本原理学习

在看本博文之前,最好阅读如下三篇文章: 1. Discriminative Training and Maximum Entropy Models for Statistical Machine Translation 2. Minimum Error Rate Training in Statistical Machine Translation 3. Z-MERT: A Fully Configurable Open Source Tool for Minimum Error Rate Tr

Jvm 内存浅析 及 GC个人学习总结

从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C语言的同学都知道,在C语言中内存的开辟和释放都是由我们自己来管理的,每一个new操作都要对于一个delete操作,否则就会参数内存泄漏和溢出的问题,导致非常槽糕的后果.但在Java开发过程中,则完全不需要担心这个问题.因为jvm提供了自动内存管理的机制.内存管理的工作由jvm帮我们完成.这样我们就不

双向数据绑定---AngularJS的基本原理学习

Angular JS (Angular.JS) 是一组用来开发Web页面的框架.模板以及数据绑定和丰富UI组件.它支持整个开发进程,提供web应用的架构,无需进行手工DOM操作. AngularJS非常小,仅仅有60K,兼容主流浏览器.与 jQuery 配合良好.双向数据绑定可能是AngularJS最酷最有用的特性,将MVC的原理展现地淋漓尽致. AngularJS的工作原理是:HTML模板将会被浏览器解析到DOM中, DOM结构成为AngularJS编译器的输入. AngularJS将会遍历D

kafka基本原理学习

下载安装地址:http://kafka.apache.org/downloads.html 原文链接 http://www.jasongj.com/2015/01/02/Kafka深度解析 Kafka主要术语直观解释 BrokerKafka集群包含一个或多个服务器,这种服务器被称为broker Topic每条发布到Kafka集群的消息都有一个类别,这个类别被称为topic.(物理上不同topic的消息分开存储,逻辑上一个topic的消息虽然保存于一个或多个broker上但用户只需指定消息的top

Java学习之:JVM内存模型

一.文章来由 开始实习啦,实习转战Java开发工程师... 二.JVM内存模型总图 Java中通过多线程机制使得多个任务同时执行处理,所有的线程共享JVM内存区域main memory,而每个线程又单独的有自己的工作内存,当线程与内存区域进行交互时,数据从主存拷贝到工作内存,进而交由线程处理(操作码+操作数). 在之前,我们也已经提到,JVM的逻辑内存模型如下: 三.JVM内存模型详解 1.程序计数器 程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可

java之JVM学习--基本机构

JDK,JRE,JVM关系图 JVM所处的位置: JVM物理结构: jvm内存区详解: 程序计数器 程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 由于Java虚拟机的多线程

深入学习JVM了解JVM内存模型

我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互,而CPU运转速度越来越快,磁盘远远跟不上CPU的读写速度,才设计了内存,用户缓冲用户IO等待导致CPU的等待成本,但是随着CPU的发展,内存的读写速度也远远跟不上CPU的读写速度,因此,为了解决这一纠纷,CPU厂商在每颗CPU上加入了高速缓存,用来缓解这种症状. Jvm内存模型 虚拟机内存模型中定义的访问操作与物理计算机处理的基本一致! Java 中通过多线程机制使得多个任务同时执行处理,所有的线程共享

【JVM学习】2.Java虚拟机运行时数据区

来源: 公众号: 猿人谷 这里我们先说句题外话,相信大家在面试中经常被问到介绍Java内存模型,我在面试别人时也会经常问这个问题.但是,往往都会令我比较尴尬,我还话音未落,面试者就会"背诵"一段(Java虚拟机是由堆.方法区.虚拟机栈,吧啦吧啦...),估计心里还一脸自豪的想幸好哥提前在网上搜过,早有准备.每每这个时候,我都不忍心打断,因为"背诵"的真的太顺畅了! 这也怪不得面试者,首先Java虚拟机方面的知识,对中高级程序猿来说,工作中正面接触Java虚拟机的东西

JVM 学习总结

目录 Java内存区域 运行时数据区 & Java 内存结构 & Java 内存区域 1. 程序计数器 2. Java 虚拟机栈 3. 本地方法栈 4. 堆 5. 方法区 6. 运行时常量池 7. 直接内存 HotSpot 虚拟机对象的奥秘 对象的创建 对象的内存布局 对象的访问定位 JVM 垃圾回收 常见面试题 对象已经死亡? 1. 引用计数法 2. 可达性分析算法 3. 四种引用类型 4. 不可达对象真的死亡了吗? 5. 如何判断一个常量是废弃常量? 6. 如何判断一个类是无用的类?