Java虚拟机JVM

1. Java虚拟机的概述和基本概念

       1.1  Java虚拟机原理

所谓虚拟机,就是一台虚拟的机器。它是一款软件,用来执行一系列虚拟计算机指令,大体上虚拟机可以分为系统虚拟机和程序虚拟机,大名鼎鼎的VisualBox/VMare就属于系统虚拟机,他们完全是对物理计算机的仿真,提供了一个可运行完整操作系统的软件平台。程序虚拟机代表就是Java虚拟机,它专门为执行单个计算机程序而设计,在Java虚拟机中执行的指令,我们称为Java字节码指令。无论是系统虚拟机还是程序虚拟机,在上面运行的软件都被限制于虚拟机提供的资源中。Java发展至今,出现过很多虚拟机,最初Sun使用的一款叫Classic的Java虚拟机,到现在应用最广泛的是HotSpot虚拟机,除了Sun以外,还有BEA的JRockit,目前JRockit和HotSpot都被Oracle收入旗下,大有整合的趋势。

       1.2 Java虚拟机的基本结构及加载顺序(不同版本的jvm内存结构可能不同,这里用的是jdk8)

类加载子系统:负责从文件系统或者网络中加载Class信息,加载的信息存放在一块称之为方法区的内存空间。

方法区(所有线程共享这个区的数据)就是存放类信息、常量信息、常量池信息、包括字符串字面量和数字常量等。

Java堆:在Java虚拟机启动的时候建立Java堆,它是Java程序中最主要的k内存工作区域,几乎所有的对象实例都存放在Java堆中,堆空间是所有线程共享的。

直接内存:Java的NIO库允许Java程序使用直接内存,从而提高性能,通常内存速度会优于Java堆。读写频繁的场合可能会考虑使用。

Java栈:每个虚拟机线程都有一个私有的栈,一个线程的java栈在线程创建的时候被创建,java栈中保存着局部变量、方法参数、同时java的方法调用、返回值等。

本地方法栈:本地方法栈和Java栈非常类似,最大不同为本地方法栈用于本地方法调用。java虚拟机允许java直接调用本地方法(通常使用C编写)

垃圾回收系统:垃圾收集系统是Java的核心,也是必不可好的,java有一套自己进行垃圾清理的机制,开发人员无需手工清理,文章下面会有详细说明。

PC寄存器:(Program Counter)寄存器也会每个线程私有的空间,java虚拟机会为每个线程创建PC寄存器,在任意时刻,一个Java线程总是在执行一个方法,这个方法被称为当前方法,如果当前方法不是本地方法,PC寄存器就会执行当前正在执行的命令,如果是本地方法,则PC寄存器值为undefined,寄存器存放如当前执行环境指针、程序计数器、操作栈指针、计算的变量指针等信息。

执行引擎:虚拟机最核心的组件就是执行引擎了,它负责执行虚拟机的字节码。一般用户先进行编译成机器码后执行。

2. 堆、栈、方法区

堆:解决的是数据存储的问题,即程序如何执行,或者说如何处理数据。

栈:解决程序的运行问题,即程序如何执行,或者说如何处理数据。

方法区:辅助堆栈的快永久区,解决堆栈信息的产生,是先决条件。

我们创建一个新的对象,User:那么User类的一些信息(类信息、静态信息都存在于方法区中)

  1.      而User类被实例化出来之后,被存储到Java堆中,一块内存空间
  2.      当我们去使用的会后,都是使用User对象的引用,形如User user = new User();
  3.      这里的user就是存放在java栈中的,即User真实对象的一个引用。

简单来说:存对象,存对象的引用,方法区(线程共享)存的是对象中的常量数据


这里插一个知识点:数据共享,线程共享  (https://blog.csdn.net/qq_39404258/article/details/82459543)

数据共享的有:栈、寄存器、PC                        线程共享的有:堆、全局变量、静态变量、方法区


2.1 辨清java堆

java堆是和java应用程序关系最密切的内存空间,几乎所有的对象都存放在堆中,并且java堆完全是自动化管理的,通过垃圾回收机制,垃圾对象会自动清理,不需要显示地释放。

根据垃圾回收机制不同,java堆有可能拥有不同的结构。最为常见的就是将整个java堆分为新生代和老年代。其中新生代存放新生对象或者年龄不大的对象,老年代则存放老年对象。

新生代分别为eden(伊甸园)区,s0区,s1区,s0和s1也被称为from和to区域,他们是两块大小相等并且可以互换角色的空间。绝大多数情况下,对象首先分配在eden区,在一次新生代回收后,如果对象还存活,则会进入s0或者s1区,之后每经过一次新生代回收,如果对象存活则它的年龄就加1,当对象达到一定年龄后,则进入老年代(tenured)

s0 和 s1 大小一样,并且可以相互转换角色。

如何确定新生代对象、老年代对象:

因为java有自动的垃圾回收机制,每当回收对象一次该对象就改变一次标识,相当于标识对象的年龄。

java中新生代的from(s0)和to(s1)空间就是使用,复制算法:其核心思想就是将内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存留对象复制到未被使用的内存块中去,之后去清除之前正在使用的内存块中所有的对象,反复去交换两个内存的角色,完成垃圾收集。

2.2 java栈

java栈是一块线程私有的内存空间,一个栈,一般由三部分组成:局部变量表、操作数栈和帧数据区。

局部变量表:用户报错函数的参数及局部变量。

操作数栈:主要保存计算过程的中间结果,同事作为计算过程中变量临时的存储空间。

帧数据:除了局部变量表和操作数栈以外,栈还需要一些数据来支持常量池的解析,这里帧数据区保存着访问常量池的指针,方便程序访问常量池,另外,当函数返回或者出现异常时,虚拟机必须有一个异常处理表,方便发送异常的时候找到异常的代码,因此异常处理表也是帧数据区的一部分。

2.3 Java方法区(Perm)

Java方法区和堆一样,方法区是一块所有线程共享的内存区域,它保存系统的类信息,比如类的字段、方法、常量池等。方法区的大小决定了系统可以保存多少个类,如果系统定义太多的类,导致方法区溢出。虚拟机同样会抛出内存溢出错误。方法区可以理解为永久区(Perm) 。


3. 了解虚拟机参数

在虚拟机运行的过程中,如果可以跟踪系统的运行状态,那么对于问题的故障排查会有一定的帮助,为此,虚拟机提供了一些跟踪系统状态的参数,使用给定的参数执行java虚拟机,就可以在系统运行事时打印相关日志,用于分析实际问题。我们进行虚拟机参数配置,其实主要就是围绕着堆、栈、方法区进行配置。

垃圾回收机制可以配置不同的垃圾回收策略。

3.1 堆分配参数(一)

-XX:+PrintGC 使用这个参数,虚拟机启动后,只要遇到GC就会打印日志。
-XX:+UseSerialGC 配置串行回收器。
-XX:+PrintGCDetails 可以查看信息信息,包括各个区的情况。
-Xms:设置Java应用程序启动时的初始堆大小
-Xmx:设置Java应用程序能获得的最大堆大小
-Xmx20m -Xms5m -XX:+PrintConnandLineFlags: 设置最大堆内存20M,初始化堆内存为5M,可以将隐式或者显示传给虚拟机的参数输出。
示例[Test01]
总结:在实际工作中,我们可以直接将初始化的堆大小与最大堆大小设置相等,这样的好处是可以减少程序运行时的垃圾回收次数,从而提高性能。

3.1.1 在哪里配置 ? 先来个初级的(先不说在linux上的设置),在idea 或者 eclipse中配置(eclipse与idea差不多)。

如果是eclipse则点击右键,run as --> configuration

下面是idea图:

 设置参数为:

-XX:+PrintGC -Xmx20m -Xms5m -XX:+UseSerialGC -XX:+PrintGCDetails

 Test01代码:

package com.jvm;

/**
 * @author: wangqinmin
 * @date: 2019/7/23 14:50
 * @description: 仰天大笑出门去,我辈岂是蓬蒿人
 */
public class Test01 {
    public static void main(String[] arge) {

        /**
         * -XX:+PrintGC 使用这个参数,虚拟机启动后,只要遇到GC就会打印日志。
         * -XX:+UseSerialGC 配置串行回收器。
         * -XX:+PrintGCDetails 可以查看信息信息,包括各个区的情况。
         * -Xms:设置Java应用程序启动时的初始堆大小
         * -Xmx:设置Java应用程序能获得的最大堆大小
         * -Xmx20m -Xms5m -XX:+PrintConnandLineFlags: 设置最大堆内存20M,初始化堆内存为5M,可以将隐式或者显示传给虚拟机的参数输出。
         * 示例[Test01]
         * 总结:在实际工作中,我们可以直接将初始化的堆大小与最大堆大小设置相等,这样的好处是可以减少程序运行时的垃圾回收次数,从而提高性能。
         */

        // 使用这个参数进行配置。
        //-XX:+PrintGC -Xmx20m -Xms5m -XX:+UseSerialGC -XX:+PrintGCDetails
        System.out.println("jvm启动后遇到GC就会打印日志,设置最大堆内存20M,初始化堆内存5M,使用串行回收器,查看jvm各个区的情况");
        double a = (double) Runtime.getRuntime().maxMemory();
        System.out.println("最大内存:" + a / 1024 / 1024 / 1024 + "G");
        System.out.println();


        // 查看GC信息
        System.out.println("max memory:" + Runtime.getRuntime().maxMemory() + "B,转换为KB:" + Runtime.getRuntime().maxMemory() / 1024 + "KB,转换为M:" + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "M");
        System.out.println("free memory:" + Runtime.getRuntime().freeMemory() + "B,转换为KB:" + Runtime.getRuntime().freeMemory() / 1024 + "KB,转换为M:" + Runtime.getRuntime().freeMemory() / 1024 / 1024 + "M");
        System.out.println("total memory:" + Runtime.getRuntime().totalMemory() + "B,转换为KB:" + Runtime.getRuntime().totalMemory() / 1024 + "KB,转换为M:" + Runtime.getRuntime().totalMemory() / 1024 / 1024 + "M");
        System.out.println();

        // 查看完信息后,实例化一个1M大小的对象
        byte[] b1 = new byte[1 * 1024 * 1024];

        // 查看GC信息
        System.out.println("示例化一个1M的对象");
        System.out.println("max memory:" + Runtime.getRuntime().maxMemory() + "B,转换为KB:" + Runtime.getRuntime().maxMemory() / 1024 + "KB,转换为M:" + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "M");
        System.out.println("free memory:" + Runtime.getRuntime().freeMemory() + "B,转换为KB:" + Runtime.getRuntime().freeMemory() / 1024 + "KB,转换为M:" + Runtime.getRuntime().freeMemory() / 1024 / 1024 + "M");
        System.out.println("total memory:" + Runtime.getRuntime().totalMemory() + "B,转换为KB:" + Runtime.getRuntime().totalMemory() / 1024 + "KB,转换为M:" + Runtime.getRuntime().totalMemory() / 1024 / 1024 + "M");
        System.out.println();

        // 查看完信息后,实例化一个4M大小的对象
        byte[] b2 = new byte[4 * 1024 * 1024];

        // 查看GC信息
        System.out.println("示例化一个4M的对象");
        System.out.println("max memory:" + Runtime.getRuntime().maxMemory() + "B,转换为KB:" + Runtime.getRuntime().maxMemory() / 1024 + "KB,转换为M:" + Runtime.getRuntime().maxMemory() / 1024 / 1024 + "M");
        System.out.println("free memory:" + Runtime.getRuntime().freeMemory() + "B,转换为KB:" + Runtime.getRuntime().freeMemory() / 1024 + "KB,转换为M:" + Runtime.getRuntime().freeMemory() / 1024 / 1024 + "M");
        System.out.println("total memory:" + Runtime.getRuntime().totalMemory() + "B,转换为KB:" + Runtime.getRuntime().totalMemory() / 1024 + "KB,转换为M:" + Runtime.getRuntime().totalMemory() / 1024 / 1024 + "M");
        System.out.println();

        /**
控制台打印数据:
         *"C:\Program Files\Java\jdk1.8.0_152\bin\java.exe" -XX:+PrintGC -Xmx20m -Xms5m -XX:+UseSerialGC -XX:+PrintGCDetails "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.1.4\lib\idea_rt.jar=6067:C:\Program Files\JetBrains\IntelliJ IDEA 2018.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_152\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\rt.jar;E:\2019MyProject\dust\out\production\dust1;C:\Program Files\JetBrains\IntelliJ IDEA 2018.1.4\lib\junit-4.12.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2018.1.4\lib\hamcrest-core-1.3.jar" com.jvm.Test01
         * [GC (Allocation Failure) [DefNew: 1664K->191K(1856K), 0.0010359 secs] 1664K->672K(5952K), 0.0010621 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
         * 最大内存:0.0189208984375G
         *
         * max memory:20316160B,转换为KB:19840KB,转换为M:19M
         * free memory:4973104B,转换为KB:4856KB,转换为M:4M
         * total memory:6094848B,转换为KB:5952KB,转换为M:5M
         *
         * 示例化一个1M的对象
         * max memory:20316160B,转换为KB:19840KB,转换为M:19M
         * free memory:3924512B,转换为KB:3832KB,转换为M:3M
         * total memory:6094848B,转换为KB:5952KB,转换为M:5M
         *
         * [GC (Allocation Failure) [DefNew: 1661K->76K(1856K), 0.0011371 secs][Tenured: 1696K->1773K(4096K), 0.0013115 secs] 2142K->1773K(5952K), [Metaspace: 3412K->3412K(1056768K)], 0.0024785 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
         * 示例化一个4M的对象
         * max memory:20316160B,转换为KB:19840KB,转换为M:19M
         * free memory:4285616B,转换为KB:4185KB,转换为M:4M
         * total memory:10358784B,转换为KB:10116KB,转换为M:9M
         *
         * Heap
         *  def new generation   total 1920K, used 79K [0x00000000fec00000, 0x00000000fee10000, 0x00000000ff2a0000)
新生代中的eden区:
         *   eden space 1728K,   4% used [0x00000000fec00000, 0x00000000fec13cc0, 0x00000000fedb0000)
新生代中的s0区:
         *   from space 192K,   0% used [0x00000000fedb0000, 0x00000000fedb0000, 0x00000000fede0000)
新生代中的s1区:
         *   to   space 192K,   0% used [0x00000000fede0000, 0x00000000fede0000, 0x00000000fee10000)
老年代区tenured:
         *  tenured generation   total 8196K, used 5869K [0x00000000ff2a0000, 0x00000000ffaa1000, 0x0000000100000000)
         *    the space 8196K,  71% used [0x00000000ff2a0000, 0x00000000ff85b520, 0x00000000ff85b600, 0x00000000ffaa1000)
元空间Metaspace:
         *  Metaspace       used 3461K, capacity 4500K, committed 4864K, reserved 1056768K
类空间 class space:
         *   class space    used 374K, capacity 388K, committed 512K, reserved 1048576K
         *
         * Process finished with exit code 0
         */
    }
}

 然后确定即可,开始运行Test01程序。

3.2 堆分配参数(二)

新生代的配置

-Xmn: 可以设置新生代的大小,设置一个比较大的新生代会减少老年代的大小,这个参数对系统性能以及GC行为有很大影响,新生代大小一般会设置整个堆空间的1/3到1/4左右。
-XX:SurvivorRatio: 用来设置新生代中eden空间和from/to空间的比例。含义:-XX:SurvivorRatio=eden/from=eden/to

示例【Test02】

package com.jvm;

/**
 * @author: wangqinmin
 * @date: 2019/7/23 17:57
 * @description: 仰天大笑出门去,我辈岂是蓬蒿人
 */
public class Test02 {
    public static void main(String[] args) {
        // 第一次配置:
        // -Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

        // 第二次配置:
        // -Xms20m -Xmx20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

        // 第三次配置:
        // -XX:NewRatio=老年代/新生代
        // -Xms20m -Xmx20m -XX:NewRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

        byte[] b = null;
        // 连续向系统申请10M空间
        for (int i = 0; i < 10; i++) {
            b = new byte[1 * 1024 * 1024];
        }
    }
}

总结:不同的堆分布情况,对系统执行会产生一定影响,在实际工作中,应该根据系统的特点做出合理的配置,基本策略:尽可能将对象预留在新生代,减少老年代的GC次数。

除了可以设置新生代的绝对大小(-Xmn),还可以使用(-XX:NewRatio)设置新生代和老年代的比例:-XX:NewRatio=老年代/新生代

第一次配置:
-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

第二次配置:
-Xms20m -Xmx20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

第三次配置:
-XX:NewRatio=老年代/新生代
-Xms20m -Xmx20m -XX:NewRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

 某些参数认识:

-Xss:设置线程栈的大小
-XX:MinHeapFreeRatio:设置堆空间最小空闲比例。当对空间的空闲内存小于这个数值时,JVM便会扩展堆空间
-XX:MaxHeapFreeRatio:设置堆空间的最大空闲比例。当堆空间的空闲内存大于这个数值时,便会压缩堆空间,得到一个较小的堆
-XX:NewSize:设置新生代的大小
-XX:NewRatio:设置老年代与新生代的比例,它等于老年代大小除以新生代大小
-XX:SurvivorRatio:新生代中eden区与survivior区的比例
-XX:MaxPermSize:设置最大的持久区的大小
-XX:PermSize:设置永久区的初始值
-XX:TargetSurvivorRatio:设置survivior区的可使用率。当survivior区的空间使用率达到这个数值时,会将对象送入老年代

3.3 堆内存溢出处理

在java程序的运行过程中,如果堆空间不足,则会抛出内存溢出的错误( Out Of Menory)OOM,一旦这类问题发生在生产环境,可能引起严重的业务中断,Java虚拟机提供了-XX:+HeapDumpOnOutOfMemoryError,使用该参数可以在内存溢出时导出整个堆信息,与之配合使用的还有参数-XX:HeapDumpPath=d:/Test03.dump 这个相当于设置导出具体位置,并使用java自带的工具jdk的bin目录下的jvisualvm.exe打开文件,进行阅览。

示例【Test03】

package com.jvm;

import java.util.Vector;

/**
 * @author: wangqinmin
 * @date: 2019/7/23 18:11
 * @description: 仰天大笑出门去,我辈岂是蓬蒿人
 */
public class Test03 {

    /**
     * 使用jvisualvm来分析dump文件:
     * jvisualvm是JDK自带的Java性能分析工具,在JDK的bin目录下,文件名就叫jvisualvm.exe。
     * jvisualvm可以监控本地、远程的java进程,实时查看进程的cpu、堆、线程等参数,对java进程生成dump文件,并对dump文件进行分析。
     * 像我这种从服务器上dump下来文件也可以直接扔给jvisualvm来分析。
     * 使用方式:直接双击打开jvisualvm.exe,点击文件->装入,在文件类型那一栏选择堆,选择要分析的dump文件,打开。
     *
     * @param args
     */
    public static void main(String[] args) {
        // 内存溢出和内存溢出原因存放位置
        // -Xms2m -Xmx2m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/Test03.dump
        Vector v = new Vector();
        for (int i = 0; i < 5; i++) {
            v.add(new Byte[1 * 1024 * 1024]);
        }
    }
}

3.4  栈调用深度

package com.jvm;

/**
 * @author: wangqinmin
 * @date: 2019/7/23 19:11
 * @description: 仰天大笑出门去,我辈岂是蓬蒿人
 */
public class Test04 {

    // -Xss1m
    // 调用最大深度:21047
    // -Xss5m
    // 调用最大深度:123178
    /**
     * 栈调用深度
     */
    private static int count;

    public static void main(String[] args) {
        try {
            recursion();
        } catch (Throwable t) {
            System.out.println("调用最大深度:" + count);
            t.printStackTrace();
        }
    }

    public static void recursion() {
        count++;
        recursion();
    }
}

3.5 方法区

方法区和java堆一样,方法区是一块所有线程共享的内存区域,它用于保存系统的类信息, 方法区(永久区)可以保存多少信息可以对其进行配置,在默认情况下,-XX:MaxPermSize为64M, 如果系统运行时生产大量的类,就需要设置一个相对合适的方法,以免出现永久区内存溢出的问题。

-XX:PermSize=64M -XX:MaxPermSize=64M

3.6 直接内存

直接内存配置(据说jdk1.7之后可以不用配置): 直接内存也是java程序中非常重要的组成部分,特别是广泛用在NIO中,直接内存跳过了java堆,是java程序可以直接访问原生堆空间, 因此在一定程度上加快了内存空间的访问速度,但是说直接内存一定就可以提高内存访问速度也不见得,具体情况具体分析。 相关配置参数:-XX:MaxDirectMemorySize,如果不设置默认值为最大堆空间即-Xmx.直接内存使用达到上限时,就会触发垃圾回收,如果 不能有效的释放空间,也会引起系统的OOM(Out of Memory 内存泄漏)。

简单说一下:jdk1.7以后就没有Client 和 Server 虚拟机工作模式了。 Client 与 Server 模式的区别; Client模式启动快,运行期间性能低,基本做测试使用。 Server模式启动慢,优化策略较好,运行性能远远快与Client模式。

与JVM不错的博客(有总结,了解): https://www.cnblogs.com/redcreen/archive/2011/05/04/2036387.html

4. 垃圾回收概念和算法、及对象的分代转换

垃圾回收,简称GC,需要先澄清什么是垃圾,类比日常生活中的垃圾,我们会把他们丢入垃圾桶,然后倒掉,GC中的垃圾,特指存在于内存中、不会再被使用的对象,而回收就是相当于把垃圾 “倒掉” 。垃圾回收有很多算法:如引用计数算法、标记压缩算法、复制算法、分代、分区的思想。

进一步了解垃圾回收算法:

https://blog.csdn.net/newchenxf/article/details/78071804 

5. 垃圾收集器 

6. Tomcat性能影响实验

7. 性能监控工具

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值