Java虚拟机的内存模型分为五部分:程序计数器、Java虚拟机栈、本地方法栈、堆、方法区。

程序计数器、Java虚拟机栈、本地方法栈都是线程私有的,也就是每个线程都拥有这三个区域,而且这三个区域会随着线程的创建而开始,随着线程的结束而销毁,那么垃圾收集器在何时清理这三个区域的问题就解决了。

堆和方法区是所有线程共享的区域,并且是在JVM开启的时候创建一直运行到JVM停止,因此它没办法根据线程的创建而创建,根据线程的结束而结束。堆中存放JVM在运行期间的所有对象,虽然每个对象的内存大小在加载该对象所属类的时候就已经确定了,但是究竟创建多少个这样的对象只能在程序运行期间才能知道;方法区中存放的类信息、静态成员变量和常量,类的加载是在程序运行过程中,当需要创建这个类的对象时才会加载这个类,JVM究竟需要加载多少个类也需要在程序运行期间才能确定。

一、堆内存的回收

  1. 如何判断那些对象需要被回收

首先判断一个对象无效的依据是这个对象不再被任何的对象或者变量所引用。

判断的方法主要有两种:引用计数法和可达性分析法;

引用计数法就是每一个对象都会有一个引用计数器,当给对象被引用一次计数器就加1,当断开一个引用计数器就减1,所以当引用计数器的值为0 的时候就表示该对象没有被任何的对象或者变量引用。

可达性分析法:所有的和GC Roots直接或者间接关联的对象都是有效对象,没有关联的都是无效对象,这里的GC Roots指的是:Java虚拟机栈中所引用的对象、本地方法区中静态属性所引用的对象、方法区中常量所引用的对象、本地方法栈中所引用的对象。

注意:引用计数法虽然简单但是存在一个严重的问题就是无法解决循环引用的问题。因此普遍使用的是可发性分析法判断一个对象是否有效。

  1. 回收无效对象的过程是什么

a)        首先判断这个对象是否覆盖了finalize()方法:如果这个对象没有覆盖这个方法,直接释放对象内存,如果该对象覆盖了这个方法并且这个方法还没有被执行,那么就将finalize()方法放到F-Queue队列中,

b)        执行F-Queue队列中的finalize()方法:虚拟机以较低的优先级执行这些finalize()方法,也不会确保所有的finalize()方法都会被执行,如果finalize()方法出现耗时情况虚拟机会直接停止执行并释放内存。

c)        对象重生或者死亡:如果执行finalize()方法的时候,this对象被别的对象或者变量引用,那么该对象就重生了,否者就会被直接释放内存。

二、方法区的内存回收

  1. 方法区中主要清楚的对象:废弃的常量和废弃的类
  2. 如何判断废弃常量:判断废弃的常量和废弃的对象是一样的,只要这个常量不再被任何的对象或者变量所引用,那么该常量就是一个废弃常量。
  3. 如何判断废弃的类:

a)        判断废弃的类要求的条件比较复杂

b)        该类多创建的所有对象都已经被回收

c)        加载该类的ClassLoader类已经被回收

d)        给类的Class类没有被任何的对象或者变量所引用。

三、垃圾收集算法

算法名称

算法的介绍

优点

缺点

引用区域

标记-清除算法

利用前面的判断方法判断出需要清除哪些数据,并给他们做上标记,然后清除被标记的数据。

该算法标记和清除的过程效率都很低,清除后会有大量的碎片空间,导致无法存储大对象,降低空间利用率。

复制算法

将内存分成两份,只在其中一块上存储数据,当需要回收垃圾时,也是首先标记出废弃的数据,然后将有用的数据复制到另一部分内存,再把第一块内存全部清除。

这种算法避免了碎片空间

但是内存被缩小一半,因为每次都要将有用的数据全部复制一遍,效率不高

新生代中使用:为了防止内存碎片化:堆内存的新生代进一步划分为:Eden区+Survior1区+Survior2区

标记-整理算法

在回收垃圾之前首先将所有被废弃的对象做上标记,然后将所有未被标记的对象移动到另一边,最后清空另一边区域即可

这是一种老年代的垃圾收集算法

分代收集算法

将内存划分为新生代和老年代。新生代中存放“朝生夕死”的对象,老年代中存放寿命较长的对象,然后再不同的区域使用不同的垃圾收集算法

四、如何解决空间利用率的问题:

在新生代中,由于大量的对象都是“朝生夕死”,也就是一次垃圾收集后只有少量对象存活,因此我们可以将内存划分成三块:Eden、Survior1、Survior2,内存大小分别是8:1:1。分配内存时,只使用Eden和一块Survior1。当发现Eden+Survior1的内存即将满时,JVM会发起一次MinorGC,清除掉废弃的对象,并将所有存活下来的对象复制到另一块Survior2中。那么,接下来就使用Survior2+Eden进行内存分配。通过这种方式,只需要浪费10%的内存空间即可实现带有压缩功能的垃圾收集方法,避免了内存碎片的问题。

五、什么叫做分配担保:

当JVM准备为一个对象分配内存空间时,发现此时Eden+Survior中空闲的区域无法装下该对象,那么就会触发MinorGC,对该区域的废弃对象进行回收。但如果MinorGC过后只有少量对象被回收,仍然无法装下新对象,那么此时需要将Eden+Survior中的所有对象都转移到老年代中,然后再将新对象存入Eden区。这个过程就是“分配担保”。

六、老年代为什么不使用复制算法而要使用标记-整理算法:

老年代中存放的对象一般寿命都比较长,因此每次垃圾回收之后都会有大量的对象存活,因此如果选用复制算法的话,每次需要复制大量的对象,会导致效率极低,而且在新生代中使用“复制算法”,当Eden+Survior中都装不下某个对象时,可以使用老年代的内存进行“分配担保”,而如果在老年代使用该算法,那么在老年代中如果出现Eden+Survior装不下某个对象时,没有其他区域给他作分配担保。因此,老年代中一般使用“标记-整理”算法。

七、Java中引用的种类:根据声明周期的长短,将引用分为四类:强引用、软引用、弱引用、虚引用。这四类引用的声明周期依次递减。

深入理解JVM(3)——垃圾收集策略详解的更多相关文章

  1. 理解JVM之垃圾收集器详解

    前言 垃圾收集器作为内存回收的具体表现,Java虚拟机规范并未对垃圾收集器的实现做规定,因而不同版本的虚拟机有很大区别,因而我们在这里主要讨论基于Sun HotSpot虚拟机1.6版本Update22 ...

  2. 深入理解JVM(三)——垃圾收集策略具体解释

    Java虚拟机的内存模型分为五个部分.各自是:程序计数器.Java虚拟机栈.本地方法栈.堆.方法区. 这五个区域既然是存储空间,那么为了避免Java虚拟机在执行期间内存存满的情况,就必须得有一个垃圾收 ...

  3. 深入理解JVM内存分配策略

    理解JVM内存分配策略 三大原则+担保机制 JVM分配内存机制有三大原则和担保机制 具体如下所示: 优先分配到eden区 大对象,直接进入到老年代 长期存活的对象分配到老年代 空间分配担保 对象优先在 ...

  4. JVM之内存结构详解

    对于开发人员来说,如果不了解Java的JVM,那真的是很难写得一手好代码,很难查得一手好bug.同时,JVM也是面试环节的中重灾区.今天开始,<JVM详解>系列开启,带大家深入了解JVM相 ...

  5. JVM性能调优详解

    前面我们学习了整个JVM系列,最终目标的不仅仅是了解JVM的基础知识,也是为了进行JVM性能调优做准备.这篇文章带领大家学习JVM性能调优的知识. 性能调优 性能调优包含多个层次,比如:架构调优.代码 ...

  6. [转帖]JVM性能调优详解

    JVM性能调优详解 https://www.cnblogs.com/secbro/p/11833651.html 应该是 jdk8 以前的方法 貌似permsize 已经放弃这一块了. 前面我们学习了 ...

  7. 七牛云存储Python SDK使用教程 - 上传策略详解

    文 七牛云存储Python SDK使用教程 - 上传策略详解 七牛云存储 python-sdk 七牛云存储教程 jemygraw 2015年01月04日发布 推荐 1 推荐 收藏 2 收藏,2.7k  ...

  8. guava-retrying 源码解析(等待策略详解)

    一.等待策略相关类: 1.等待策略接口:WaitStrategy接口 该接口只有一个方法,就是返回尝试失败之后,下一次尝试之前的等待时间.long computeSleepTime(Attempt f ...

  9. [转帖]Nginx服务器的六种负载均衡策略详解

    Nginx服务器的六种负载均衡策略详解 咔咔侃技术 2019-09-11 17:40:12 一.关于Nginx的负载均衡 在服务器集群中,Nginx起到一个代理服务器的角色(即反向代理),为了避免单独 ...

随机推荐

  1. MyBatis - 1.入门

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架. MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集. MyBatis可以使用简单的XML或注解 ...

  2. Linux下Nginx安装/启动/重启/停止

    Nginx是高性能的web服务器也是非常好用反向代理服务器,可以实现负载均衡,动静分离等策略,在linux下用的非常多.下面是下载地址   http://nginx.org/en/download.h ...

  3. C# 属性(Property)和字段(Field)的区别

    导读: 近期学习过程中发现了一些问题,我的学习只是学习,敲代码就是敲代码,没有加入思考,也不问为什么就直接去敲人家写好的例子去敲,把知识都学死了,逐渐散失了思考能力,所以学习的兴趣大打折扣,正如那句话 ...

  4. 使用docker方式安装etcd集群,带TLS证书

    网上文档也多,安装的时候,还是踩了几个坑. 现在作一个安装记录吧. 1,先作自签名的证书ca-csr.json(为了和k8s共用根证书,可能将信息调为k8s). { "CN": & ...

  5. Windows Azure 搭建网络代理 Proxy

    额 题目起的有点大 其实就是在 Linux 上使用代理 不过是用的 Azure 上的 Liunx 虚拟机而已 如何在 Azure 上搭建 VPN 见上篇:http://www.cnblogs.com/ ...

  6. ajax请求头加Token时发生的跨域(CORS)请求问题

    首先描述下问题:需求是在请求头中加入token,我在ajax请求数据时添加了请求头‘Authorization’字段,并添加了响应的token值,在请求数据的时候浏览器报错如下: Request he ...

  7. PyCharm安装使用 激活码

    从pycharm官网 [http://www.jetbrains.com/pycharm/download/#section=windows]下载完整安装包 激活码激活 优点:Window.Mac.U ...

  8. CAS和ABA问题

    一.引言                                                                                                 ...

  9. python 格式话-占位符

    格式化输出:name = qjage = 30job = itsalary = 6000例1:字符串拼接方法,不建议,因为会在内存中开辟多块内存空间. info = '''---------- inf ...

  10. python--return小练习

    #返回单个值,return a:#一个return后的语句不再执行,def calc_sum(*args): ax = 0 for n in args: ax = ax + nprint(ax); r ...