| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> JVM 和垃圾回收机制 -> 正文阅读 |
|
[Java知识库]JVM 和垃圾回收机制 |
一、JVM1.1、JVM 运行参数1.1.1、标准参数JVM 的标准参数,可以通过
设置系统属性
idea设置系统属性 JVM 虚拟机的运行模式有两种,在 32 位的虚拟机中支持 client 和 server 两种模式,而 64 位的虚拟机只支持 server 模式。 两种模式的区别:
虚拟机的启动模式可以通过
1.1.2、-X 参数
实战:调整堆大小
默认的设置
查看默认的堆设置
根据具体的情况适当的调整堆内存大小,可以充分利用服务器资源,让程序运行的更快。 1.1.3、-XX 参数
非 boolean 类型
实战:禁用手动调用 GC
1.1.4、查看虚拟机运行参数 使用
看正在运行的虚拟机参数 要查看正在运行的服务启用了哪些虚拟机参数,可使用
1.2、Java 内存区域Java 虚拟机在执行 Java 代码的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。 根据《Java 虚拟机规范》规定,Java 虚拟机所管理的内存将会包括以下几个运行时数据区。 1.2.1、程序计数器程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。 由于 Java 虚拟机的多线程时通过线程轮流切换并分配处理器执行时间的方式实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能够恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。 如果线程正在执行一个 Java 方法,这个计数器记录的正是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器的值则为空(undefined)。此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。 1.2.2、虚拟机栈与程序计数器一样,Java 虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是 Java 方法的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法从调用直到执行完成的过程,就对于着一个栈帧在虚拟机栈中入栈到出栈的过程。 经常有人把 Java 内存分为堆内存(Heap)和栈内存(Stack),这种分法比较粗糙,Java 内存区域的划分实际上远比这复杂。这种划分方式的流行只能说大多数程序员最关注的、与对象内存分配关系最密切的内存区域是这两块。其中所指的“堆”后面会专门讲到。而所指的“栈”就是现在讲的虚拟机栈,或者说是虚拟机栈中局部变量表部分。 局部变量表存放了编译器可知的各种基本数据类型(boolean、byte、short、char、int、long、float、double)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和 returnAddress 类型(指向了一条字节码指令的地址)。 其中 64 位长度的 long 和 double 类型的数据会占用 2 个局部变量空间(slot),其余的数据类型只占用 1 个。局部变量表所需要的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间时完全确定的,在方法运行期间不会改变局部变量表的大小。 在 Java 虚拟机规范中,对这个区域规定了两种异常情况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常;如果虚拟机栈可以动态扩展(当前大部分的 Java 虚拟机都可以动态扩展,只不过 Java 虚拟机规范中也允许固定长度的虚拟机栈),如果扩展时无法申请到足够的内存,就会抛出 OutOfMemoryError 异常。 1.2.3、本地方法栈本地方法栈与虚拟机栈所发挥的作用是非常相似的,他们之间的区别不过时虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。在虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如 Sun HotSpot 虚拟机)直接把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出 StackOverflowError 和 OutOfMemoryError 异常。 1.2.4、Java 堆对于大多数应用来说,Java 堆(Java Heap)是 Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在 Java 虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配,但是随着 JIT 编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了。 Java 堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”(Garbage Colleted Heap)。从内存回收的角度来看,由于现在收集器基本都采用分代收集算法,所以 Java 堆中还可以细分为:新生代和老年代;新生代又被划分为 Eden 空间、From Survivor 空间、To Survivor 空间等。从内存分配的角度来看,线程共享的 Java 堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。不过无论如何划分,都与存放内容无关,无论哪个区域,存储的都仍然是对象实例,进一步划分的目的是为了更好的回收内存,或者更快的分配内存。 根据 Java 虚拟机规范的规定,Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过 -Xmx 和 -Xms 控制)。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出 OutOfMemoryError 异常。 1.2.5、方法区
Java 虚拟机规范对方法区的限制非常宽松,除了和 Java 堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样“永久”存在了。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,条件相当苛刻,但是这部分区域的回收确实是有必要的。在 Sun 公司的 BUG 列表中,曾出现过若干个严重的 BUG 就是由于低版本的 HotSpot 虚拟机对此区域未完全回收而导致内存泄漏。 根据 Java 虚拟机规定,当方法区无法满足内存分配需求时,将抛出 OutOfMemoryError 异常。 1.2.6、运行时常量池运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(Constant Pool Table),用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。 Java 虚拟机对 Class 文件每一部分(自然也包括常量池)的格式都有严格规定,每一个字节用于存储哪种数据都必须符合规范上的要求才会被虚拟机认可、装载和执行,但对于运行时常量池,Java 虚拟机规范没有做任何细节的要求,不同的提供商实现的虚拟机可以按照自己的需要来实现这个区域。不过,一般来说,除了保存 Class 文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。 运行时常量池相对于 Class 文件常量池的另一个重要特征是具备动态性,Java 语言并不要求常量一定只有编译器才能产生,也就是并非置入 Class 文件中常量池的内容才能进入方法区运行时常量池,运行期也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是 String 类的 intern() 方法。 既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。 1.2.7、直接内存直接内存并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域。但是这部分内存也被频繁使用,而且也可能导致 OutOfMemoryError 异常出现。 在 JDK 1.4 中新加入了 NIO 类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因此避免了在 Java 堆和 Native 堆中来回复制数据。 1.2.8、永久代《Java虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它,HotSpot 虚拟机使用 PermGen space 永久代对方法区进行落地实现,其他虚拟机并没有 PermGen space 的概念,从 JDK8 开始,HotSpot 虚拟机已经移除了永久代。其实,早在 JDK7 时,永久代的移除工作就已经开始,例如字符串常量池在 JDK7 时就已经从永久代移除。 1.2.9、元空间从 JDK8 开始,HotSpot 虚拟机彻底移除了永久代,使用元空间作为方法区的实现,元空间没有使用堆内存,而是与堆不相连的本地内存区域。所以,理论上系统可以使用的内存有多大,元空间就有多大,所以不会出现永久代存在时的内存溢出问题。 使用元空间作为方法区的实现后,原来永久代中的符号引用存储在元空间中,字符串常量池和静态类型变量存储在堆空间中。 1.2.10、为什么要移除永久代官网给出了解释:http://openjdk.java.net/jeps/122
移除永久代是为融合HotSpot JVM与 JRockit VM而做出的努力,因为JRockit没有永久代,不需要配置永久代。
1.3、VisualVMVisualVM 是一个集成了性能分析和运行监控工具的可视化程序,开发人员可通过 VisualVM 来监控和分析线程信息、查看CPU和内存的使用情况、监控 GC 等等。 在 JDK 安装目录的 bin 目录,找到 抽样器可以对 CPU、内存进行一段时间的抽样分析。 为了能够看到内存溢出效果,我们调整一下堆内存大小
二、垃圾回收机制2.1、概述垃圾收集(Garbage Collection,GC)是 Java 虚拟机的重要组成部分,它能够让软件开发人员更专注于代码实现,而不用过多的关注内存释放问题。 Java 程序在运行过程中会不断的产生一些不会再被用到的实例对象,如果不对这些对象进行清理,它们将会一直占用内存,直到某个时刻发生内存溢出的异常。GC 垃圾收集器的工作就是清理垃圾对象,整个过程无需开发人员干预。 2.2、判定对象是否可回收当一个对象无法通过任何途径使用,那这个对象就是需要清理的垃圾,最常见的垃圾判定算法是使用 “引用计数器” 或 “可达性分析”。 2.2.1、引用计数器给对象中添加一个引用计数器,每当有一个地方引用它时,计数器的值就+1;当引用失效时,计数器的值就-1;任何时刻计数器为0的对象就是不可能再被使用的。 引用计数器的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法。但是,至少在主流的 Java 虚拟机里面没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间相互循环引用的问题。 2.2.2、可达性分析主流的虚拟机实现都是通过可达性分析来判定对象是否存活的。这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象时不可用的。 在 Java 语言中,可以作为 GC Roots 的对象包含下面几种:
2.3、垃圾收集算法2.3.1、标记-清除算法算法思想 最基础的收集算法是 “标记-清除” 算法,如同它的名字一样,算法分为 “标记” 和 “清除” 两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其不足进行改进而得到的。 标记-清除算法的不足
2.3.2、复制算法算法思想 为了解决效率问题,一种称为 “复制” 的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另一块内存上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂问题,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。 复制算法的不足
2.3.3、标记-整理算法算法思想 “标记-整理” 算法是建立在 “标记-清理” 算法基础之上的,其标记过程仍然与 “标记-清理” 算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存,从而解决内存碎片化问题。
2.3.4、分代收集算法算法思想 当前商业虚拟机的垃圾收集都采用 “分代收集” 算法,这种算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对象进行分配担保,就必须使用 “标记-清理” 或者 “标记-整理” 算法来进行回收。 IBM 公司的专门研究表明,新生代中 98% 的对象都是 “朝生夕死” 的,所以并不需要按照 1:1 的比例来划分内存空间,而是将内存划分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor。当回收时,将 Eden 和 Survivor 中还存活者的对象一次性地复制到另一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。 HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8:1,也就是每次新生代中可用内存空间为整个新生代容量的 90%,只有 10% 的内存会被 “浪费”。 当然,98% 的对象可回收只是一般场景下的数据,我们没办法保证每次回收都只有不多于 10% 的对象存活,当 Survivor 空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保。 2.4、内存分配与回收策略2.4.1、对象优先在 Eden 分配大多数情况下,对象在新生代 Eden 区中分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。 Minor GC 和 Full GC 的区别:
2.4.2、大对象直接进入老年代所谓的大对象是指,需要大量连续内存空间的 Java 对象,最典型的大对象就是很长的字符串以及数组(例如 new byte[1024 * 1024 * 10] //10MB)。大对象对虚拟机的内存分配来说就是一个坏消息,经常出现大对象容易导致内存还有不少空间时,就提前除触发 GC 以获得足够的连续空间来 “安置” 它们。 虚拟机提供了一个 2.4.3、长期存活的对象将进入老年代既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。为了做到这点,虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并且对象年龄设为1.对象在 Survivor 区每 “熬过” 一次 Minor GC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为15岁),就将会被晋升到老年代中。晋升到老年代的年龄阈值,可以通过参数 2.4.4、对象年龄判断为了能更好的适应不同程序的内存情况,虚拟机并不是永远地要求对象的年龄必须达到了 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。 2.5、垃圾收集器2.5.1、概述前面我们提到了垃圾收集的算法,但还需要具体的实现,在 JVM 中,实现了多种垃圾收集器,包括:串行垃圾收集器、并行垃圾收集器、CMS 垃圾收集器、G1 垃圾收集器。 图中展示了七种不同的分代垃圾收集器,如果两个收集器之间存在连线,那么它们可以搭配使用,图中收集器所处的区域表示它们属于新生代或老年代收集器。 2.5.2、Serial 串行收集器Serial 收集器是最基础、历史最悠久的收集器,曾经是 HotSpot 虚拟机新生代收集器的唯一选择。这个收集器是一个单线程工作的收集器,当收集器工作时,必须暂停其他所有的工作线程,直到它完成收集工作。当它工作时,我们的应用程序处于暂停状态,所以我们也将这个时刻称之为 “Stop The World”。 Serial 收集器会暂停应用程序,这对交互性强的应用是不可接受的,所以一般 Web 应用不会使用该收集器。 2.5.3、ParNew 并行收集器ParNew 收集器实质上是 Serial 收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为和 Serial 收集器完全一致。 ParNew 收集器与 Serial 收集器相比并没有太多创新之处,但它却是不少运行在服务端模式下的 HotSpot 虚拟机,尤其是 JDK7 之前的遗留系统中首选的新生代收集器。 2.5.4、Parallel 收集器Parallel 收集器是 JDK8 默认的新生代垃圾收集器,它的工作机制与 ParNew 垃圾收集器是一样的,只是在此基础之上,增加了两个和系统吞吐量相关的参数,使得其使用起来更加的灵活和高效。
所谓的吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值,例如用户代码加上垃圾回收总耗时100秒,其中垃圾收集花掉了1秒钟,那吞吐量就是 99%。 高吞吐量能最大效率的利用处理器资源,低停顿时间能以更快的响应速度来提升用户的体验。 2.5.5、Serial Old 收集器Serial Old 收集器是 Serial 收集器的老年代版本,它同样是一个单线程收集器,使用 标记-整理 算法。这个收集器主要意义也是供客户端模式下的 HotSpot 虚拟机使用。 2.5.6、Parallel Old 收集器Parallel Old 收集器是 Parallel 收集器的老年代版本,支持多线程并发收集,基于 标记-整理 算法实现。 Parallel Old 收集器直到 JDK6 时才开始提供,在此之前,新生代的 Parallel 收集器只能与 Serial Old 收集器配合工作,由于 Serial Old 收集器在服务端在性能上的拖累(单线程无法充分利用服务器多处理器的并行处理能力),这种组合的总吞吐量甚至没有 ParNew + CMS 的组合来的优秀。 直到 Parallel Old 收集器 出现后,“吞吐量优先” 收集器终于有了名副其实的搭配组合,在注重吞吐量的场景可以有限考虑使用 Parallel 收集器 + Parallel Old 收集器这个组合。 2.5.7、CMS 收集器CMS(Concurrent Mark Sweep)收集器是一种老年代垃圾收集器,CMS 收集器的目标是获取最短的停顿时间,目前很大一部分互联网应用使用的就是 CMS 收集器,因为这些应用更注重给用户带来良好的交互体验。 CMS 收集器是基于 标记-清除 算法实现的,它的运作过程相对复杂,分为四个步骤,包括:
其中初始标记、重新标记着两个步骤仍然需要 “Stop The World”。初始标记仅仅只是标记一下 GC Roots 能够直接关联到的对象,速度很快; 并发标记阶段就是从 CG Roots 的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程。 重新标记阶段是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。这个阶段的停顿时间比初始标记阶段的耗时会稍微长一些。 最后是并发清理阶段,清理掉标记阶段已经判定为死亡的对象。 2.5.8、G1 收集器(重点)概述 对于垃圾回收器来说,前面收集器要么是一次性回收新生代,要么一次性回收老年代,但现在的服务器的空间已经很大了,为了优化垃圾收集操作,出现了 G1 收集器。 G1 收集器是一款软实时、低延迟、可设定目标(最大 STW 停顿时间)的垃圾收集器,用于替代 CMS,适用于较大的堆(大于4~6G),JDK9 之后默认使用 G1 收集器,其垃圾收集的时间能控制在 10 毫秒以内。(空间换时间) G1 收集器的设计原则就是简化 Java 虚拟机调优,开发人员只需要简单的三步即可完成调优:
G1 的内存布局 G1 垃圾收集器相比于其他收集器而言,最大的不同就是不再使用固定的年轻代、老年代的物理划分,取而代之的是将堆划分为若干个区域(Region),这些区域中包含了有逻辑上的年轻代、老年代区域。G1 将这些区域作为单次回收的最小单元,这样即可避免在整个堆中进行全区域的垃圾收集。 G1 能够跟踪每个区域里面的垃圾堆积价值,并在后台维护一个优先级列表,根据用户设定的允许停顿时间,优先处理回收价值收益最大的那些区域。 G1 能够自动的给 Region 分配最优的大小,如果需要手动设置,只能设置 1m, 2m, 4m, 8m, 16m, 32m 这几个值,最小是1m,最大是 32m,且必须是 2 的幂次方。 在 G1 的划分区域中,新生代的垃圾收集依然采用暂停所有用户线程的方式,将存活的对象拷贝到老年代或 Survivor 空间,G1 收集器通过将对象从一个区域复制到另一个区域完成了清理工作,这意味着 G1 在复制对象的过程中就可以完成堆内存的整理工作。 垃圾收集模式 G1 有三种垃圾收集模式,分别是 Young GC、Mixed GC、Full GC。
G1 最佳实践 不断地对期望最大停顿时间进行调优! 通过 -XX:MaxGCPauseMillis=x 来设置期望的最大 GC 停顿时间,G1 在运行的时候会根据这个参数的设置在不同的应用场景中取得吞吐量和延迟之间的最佳平衡,一般设置在 200ms 左右,不能设置的太低,比如设置为 20ms 就不行,因为过低的期望停顿时间意味着收集器的处理速度可能会跟不上垃圾产生的速度,导致垃圾的慢慢堆积,最终因占满堆而引发 Full GC,反而降低了性能。 不要设置新生代和老年代的大小! G1 收集器在运行的时候会自动的调整新生代和老年代的大小,通过改变代的大小来调整对象的晋升速度和晋升年龄,从而达到我们为收集器设置的期望暂停时间的目标。设置了新生代大小相当于放弃了 G1 为我们做的自动调优,我们只需要设置整个堆内存的大小,剩下的交给 G1 自己区分配各个代的大小。 2.5.9、垃圾收集器相关参数
2.5.10、JVM 默认的垃圾收集器JDK1.7:Parallel Scavenge (新生代)+ Parallel Old (老年代) JDK1.8:Parallel Scavenge (新生代)+ Parallel Old (老年代) JDK1.9:G1 通过
2.6、可视化 GC 日志分析工具GC Easy 是一款在线的可视化工具,易用且功能强大。
设置虚拟机参数
JVM 内存使用情况 然后依次是老年代(Old Generation)、元空间(Meta Space)、总大小(Young + Old + Meta space)。 关键性能指标
|
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 19:25:37- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |