热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

一文理清JVM和GC(上)

大家好,我是小菜,一个渴望在互联网行业做到蔡不菜的小菜。可柔可刚,点赞则柔,白嫖则刚!死鬼~看完记得给我来个三

大家好,我是小菜,一个渴望在互联网行业做到蔡不菜的小菜。可柔可刚,点赞则柔,白嫖则刚!
死鬼~看完记得给我来个三连哦!

本文主要介绍 JVM和GC解析
如有需要,可以参考
如有帮助,不忘 点赞

创作不易,白嫖无义!


一、JVM内存体系

其中方法区被JVM中多个线程共享,比如类的静态常量就被存放在方法区,供类对象之间共享。

虚拟机栈本地方法栈程序计数器是每个线程独立拥有的,不会与其他线程共享。

所以Java在通过new创建一个类对象实例的时候,一方面会在虚拟机栈中创建一个对该对象的引用,另一方面会在堆上创建类对象的实例,然后将对象引用指向该对象的实例。对象引用存放在每一个方法对应的栈帧中。

img

  • 虚拟机栈:虚拟机栈中执行每个方法的时候,都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
  • 本地方法栈:与虚拟机栈发挥的作用相似,相比于虚拟机栈为Java方法服务,本地方法栈为虚拟机使用的Native方法服务,执行每个本地方法的时候,都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
  • 方法区:它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据,方法区在JDK1.7版本及之前称为永久代,从JDK1.8之后永久代被移除。
  • 堆:堆是Java对象的存储区域,任何new字段分配的Java对象实例和数组,都被分配在了堆上,Java堆可使用 - Xms-Xmx 进行内存控制,从JDK1.7版本之后,运行时常量池从方法区移到了堆上。
  • 程序计数器:指示Java虚拟机下一条需要执行的字节码指令。

二、JAVA8之后的JVM

从图中我们可以看出JAVA8的JVM 用元空间取代了永久代

img

img

img

三、GC作用域

img

四、常见垃圾回收算法


1)引用计数法:

JVM的实现一般不采用这种方式

img

缺点:

  • 每次对对象赋值时均要维护引用计数器,且计数器本身也有一定的消耗;
  • 较难处理循环引用;

2)复制算法:

Java 堆从GC的角度可以细分为:新生代(Eden区、From Survivor区 和 To Survivor区)和 老年代。
特点:
复制算法不会产生内存碎片,但会占用空间。用于新生代。

MinorGC的过程(复制 --> 清空 --> 互换):

  1. 复制: (Eden、SurvivorFrom 复制到 SurvivorTo,年龄加1)
    首先,当Eden区满的时候会触发第一次GC,把还活着的对象拷贝到SurvivorFrom区,当Eden区再次触发GC的时候会扫描Eden区域和From区域,对这两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到To区域(如果有对象的年龄已经到达了老年的标准,则复制到老年代区),同时把这些对象的年龄加1。
  2. 清空:(清空Eden、SurvivorFrom)
    清空Eden和SurvivorFrom中的对象,也即复制之后有交换,谁空谁是to。
  3. 互换:(SurvivorTo和SurvivorFrom 互换)
    最后,SurvivorTo和SurvivorFrom 互换,原SurvivorTo成为下一次GC是的SurvivorFrom区。

3)标记清除法

算法分成标记清除两个阶段,先标记出要回收的对象,然后统一回收这些。
特点:
不会占用额外空间,但会扫描两次,耗时,容易产生碎片,用于老年代

4)标记压缩法

优点:
没有内存碎片,可以利用bump
缺点:
需要移动对象的成本,用于老年代
原理:

标记:与标记清除一样

压缩:再次扫描,并往一段滑动存活对象

五、判断对象是否可回收


1)引用计数法

Java中,引用和对象是有关联的。如果要操作对象则必须用引用进行
因此,很显然的一个方法就是通过引用计数来判断一个对象是否可以回收。简单来说就是给对象添加一个引用计数器。每当有一个地方引用它,计数器的值加1,每当有一个引用失效时,计数器的值减1。
任何时刻计数器值为0的对象就是不可能再被使用的,那么这个对象就是可回收对象。
缺点:

很难解决对象之间相互循环引用的问题

2)枚举根节点做可达性分析(根搜索路径)

所谓GC roots或者说tracing GC根集合 就是一组必须活跃的引用。
基本思路就是通过一系列名为GC Root 的对象作为起始点,从这个被称为GC Roots的对象开始向下搜索

如GC Roots没有任何引用链相连是,则说明此对象不可用。也即给定一个集合的引用作为根出发,通过引用关系

哪些可以做GCRoots对象


  • 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)
  • 方法区中的类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中N(Native方法)引用的对象

六、JVM的参数类型


1)标配参数


  • java -version

  • java -help


2)X参数


  • java -Xint -version :解释执行

  • java -Xcomp -version :第一次使用就编译成本地代码

  • java -Xmixed :混合模式


3)XX参数


  • Boolean类型

-XX:+ 或者 - 某个属性值(+:表示开启,-:表示关闭)
例子:
-XX: +PrintGCDetails: 开启打印GC收集细节
-XX: -PrintGCDetails: 关闭打印GC收集细节
-XX: +UseSerialGC: 开启串行垃圾收集器
-XX: -UseSerialGC:关闭串行垃圾收集器

  • KV设置类型

-XX: 属性key = 属性value
例子:
-XX: MetaspaceSize = 128m:设置元空间大小为128m
-XX:MaxTenuringThreshold = 15:控制新生代需要经历多少次GC晋升到老年代中的最大阈值

  • jinfo -查看当前运行程序的配置

公式:jinfo -flag 配置项 进程编号
例子:

  1. 查看初始堆大小:

  2. 查看其他参数

    img

  3. 查看使用哪种垃圾回收器

两个经典参数

  • -Xms 等价于 -XX: InitialHeapSize
  • -Xmx 等价于 -XX: MaxHeapSize

七、查看JVM默认值


  • -XX:+PrintFlagsInitial: 查看默认初始值

    • java -XX: +PrintFlagsInitial -version

    • java -XX: +PrintFlagsInitial

  • -XX:+PrintFlagsFinal :查看修改更新

    • java -XX:+PrintFlagsFinal

    • java -XX:+PrintFlagsFinal -version

    • java -XX:+PrintCommandedLineFlags


八、常用的配置参数

经典案例设置:
-Xms128m -Xmx4096m -Xss1024k -XX:Metaspacesize=512m -XX:+PrintCommandLineFlags -XX:PrintGCDetails -XX:UseSerialGC

  • -Xms

初始化大小内存,默认为物理内存1/64
等价于 -XX:InitialHeapSize

  • -Xmx

最大分配内存,默认为物理内存1/4
等价于 -XX:MaxHeapSize

  • -Xss

设置单个线程的大小,一般默认为5112K~1024K
等价于 -XX:ThreadStackSize

  • -Xmn

设置年轻代大小

  • -XX:MetaspaceSize

设置元空间大小

元空间的本质和永久代类似,都是对JVM规范中方法区的实现,不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制

  • -XX:+PrintGCdetails

输出详细的GC收集日志信息

  • -XX:SurvivorRatio

设置新生代中eden和S0/S1空间的比例
默认:
-XX:SurvivorRatio=8 --> Eden:S0:S1=8:1:1
修改:
-XX:SurvivorRatio=4 --> Eden:S0:S1=4:1:1
SurvivorRatio值就是设置eden区的比例占多少,S0/S1相同

  • -XX:NewRatio

设置年轻代与老年代在堆结构的占比
默认:
-XX:NewRatio=2: 新生代占1,老年代占2,年轻代占整个堆的1/3
修改:
-XX:NewRatio=4: 新生代占1,老年代占4,年轻代占整个堆的1/5
NewRatio值就是设置老年代的占比,剩下的1给新生代

  • -XX:MaxTenuringThreshold

设置垃圾最大年龄
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。

如果设置为0的话,则年轻代对象不经过Survivor区,直接进入老年代。对于老年代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存活时间,增加年轻代被回收的概论。

九、强软弱虚

1)强引用


  • 当内存不足,JVM开始垃圾回收,对于强引用的对象,就算出现了OOM也不会对该对象进行回收,死都不收
  • 强引用是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表名对象还活着,垃圾收集器不会碰这种对象。在Java中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的。即使该对象以后永远都不会被用到,JVM也不会回收。 因此强引用是造成Java内存泄漏的主要原因之一。
  • 对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为null,一般就是认为可以被垃圾收集(具体看垃圾收集策略)

public static void main(String[] args) {Object o1 = new Object(); //默认为强引用Object o2 = o1; //引用赋值o1 = null; //置空 让垃圾收集System.gc();System.out.println(o1); // nullSystem.out.println(o2); // java.lang.Object@1540e19d}

2)软引用


  • 软引用就是一种相对强引用弱化了一些的引用。需要用java.lang.ref.SoftReference类来实现,可以让对象豁免一些垃圾收集。
  • 系统内存充足 -> 不会回收
  • 系统内存不足 -> 会回收
  • 软引用通常用在对内存敏感的程序中,比如高速缓存就有用到软引用,内存够用的时候就保留,不够用就回收

public static void main(String[] args) {Object o1 = new Object();SoftReference softReference = new SoftReference(o1);o1 = null;System.gc();System.out.println(o1);System.out.println(softReference.get());}

3)弱引用


  • 弱引用需要用java.lang.ref.WeakReference类来实现,它比软引用的生存期更短
  • 对于弱引用的对象,只要垃圾回收机制一运行,不管JVM的内存空间是否足够,都会回收该对象占用的内存。

public static void main(String[] args) {Object o1 = new Object();WeakReference weakReference = new WeakReference(o1);o1 = null;System.gc();System.out.println(o1); //nullSystem.out.println(weakReference.get()); //null}

4)虚引用


  • 虚引用需要java.lang.ref.PhantomReference类来实现。
  • 形如虚设,它不会决定对象的生命周期。
  • 如果一个对象持有虚引用,那么它就和没有任何一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它来访问对象,虚引用必须和引用队列(ReferenceQueue)联合使用。
  • 虚引用的主要作用是跟踪对象被垃圾回收的状态,仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。PhantomReferenceget()方法总是返回null,因此无法访问对应的引用对象。其意义在于说明一个对象已经进入finalization阶段,可以被gc回收,用来实现比finalization机制更灵活的回收操作。

public static void main(String[] args) {Object o1 &#61; new Object();ReferenceQueue<Object> referenceQueue &#61; new ReferenceQueue<>();PhantomReference<Object> phantomReference &#61; new PhantomReference<>(o1,referenceQueue);System.out.println(o1); //java.lang.Object&#64;1540e19dSystem.out.println(phantomReference.get()); //nullSystem.out.println(referenceQueue.poll()); //null}

扩展&#xff1a;软弱引用适用场景

假如有一个引用需要读取大量的本地图片
存在问题&#xff1a;

  1. 如果每次读取图片都从硬盘读取则会严重影响性能。
  2. 如果一次性全部加载到内存中有可能造成内存溢出。

解决思路&#xff1a;
用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系&#xff0c;在内存不足时&#xff0c;JVM会自动回收这些缓存图片对象所占用的空间&#xff0c;从而有效地避免了OOM的问题。
Map imgMap &#61; new HashMap()

WeakHashMap&#xff1a;

public static void main(String[] args) {WeakHashMap<Integer,String> weakHashMap &#61; new WeakHashMap<>();Integer key &#61; new Integer(1);weakHashMap.put(key,"测试1");System.out.println(weakHashMap); //{1&#61;测试1}key&#61;null;System.out.println(weakHashMap); //{1&#61;测试1}System.gc();System.out.println(weakHashMap&#43;"\t"&#43;weakHashMap.size()); //{} 0}

看完不赞&#xff0c;都是坏蛋

今天的你多努力一点&#xff0c;明天的你就能少说一句求人的话&#xff01;

我是小菜&#xff0c;一个和你一起学习的男人。 &#x1f48b;


推荐阅读
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 2018年人工智能大数据的爆发,学Java还是Python?
    本文介绍了2018年人工智能大数据的爆发以及学习Java和Python的相关知识。在人工智能和大数据时代,Java和Python这两门编程语言都很优秀且火爆。选择学习哪门语言要根据个人兴趣爱好来决定。Python是一门拥有简洁语法的高级编程语言,容易上手。其特色之一是强制使用空白符作为语句缩进,使得新手可以快速上手。目前,Python在人工智能领域有着广泛的应用。如果对Java、Python或大数据感兴趣,欢迎加入qq群458345782。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Win10下游戏不能全屏的解决方法及兼容游戏列表
    本文介绍了Win10下游戏不能全屏的解决方法,包括修改注册表默认值和查看兼容游戏列表。同时提供了部分已经支持Win10的热门游戏列表,帮助玩家解决游戏不能全屏的问题。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
author-avatar
上海千恩万燮
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有