美文网首页
JVM结构及配置

JVM结构及配置

作者: 学编程的小屁孩 | 来源:发表于2020-02-21 20:52 被阅读0次

    类加载机制

    类加载器的种类
    • 启动类加载器(Bootstrap ClassLoader) :C语言实现,负责加载JRE核心类库,如JRE目标下的rt.jar,charsets.jar等。
    • 扩展类加载器(Extension ClassLoader) :负责加载JRE扩展目录ext中jar类包。
    • 应用类加载器(Application ClassLoader) :负责加载ClassPath路径下的类包 。
    • 用户自定义加载器(User ClassLoader) :负责加载用户自定义路径下的类包。
    类加载器

    类加载机制

    • 全盘负责委托机制
      该类所依赖和引用的类也由当前类的类加载器载入,除非显示使用另一个类加载器。
    • 双亲委派机制
      先委托父类加载器寻找目标类,在找不到的情况下在自己的路径中查找并载入目标类。
    类加载器加载机制

    双亲委派模式优点
    沙箱安全机制:防止核心库被篡改,自己写的String.class类不会被加载。
    避免类重复加载:父类加载器已经加载了该类时,不需要子类加载器再加载一次。

    类生命周期

    1. 加载
    将.class文件从磁盘读到内存。

    2. 链接

    • 验证:验证字节码文件的正确性(魔数)。
    • 准备:给类的静态变量分配内存,并赋予默认值
    • 解析:类装载器加载所引用的其它所有类(静态链接)。
      静态链接:解析阶段,由符号引用转行为直接引用。
      动态链接:运行阶段,由符号引用转行为直接引用。

    3. 初始化
    为类的静态变量赋予真正的初始值,执行静态代码块。

    类生命周期

    JVM运行时数据区域

    Java多线程机制使多个任务同时执行处理,所有的线程共享JVM内存区域主存,每个线程有自己的工作内存,当线程与内存区域进行交互时,数据从主存拷贝到工作内存,进而交由线程处理(操作码+操作数)。

    • 线程私有:程序计数器、虚拟机栈、本地方法栈,线程结束就释放不用垃圾回收
    • 线程共享:堆、元数据区(方法区)。
    JVM内存模型

    1、程序计数器(Program Counter Register)

    程序计数器是一块较小的内存空间,指向方法区中字节码。字节码解释器通过改变这个计数器的值来选取下一条需要执行字节码指令(分支、循环、跳转、异常处理、线程恢复等)。

    • 通过轮流切换并分配处理器执行时间实现多线程,各线程之间的计数器互不影响。
    • 如果执行一个Java 方法,计数器记录正在执行的虚拟机字节码指令的地址。
    • 如果正在执行Natvie 方法,这个计数器值则为空Undefined。

    该区域异常状况:
    唯一没有任何OutOfMemoryError 情况的区域。

    程序计数器

    2、Java虚拟机栈(Java Virtual Machine Stacks)

    虚拟机栈描述的是Java方法执行的内存模型,每个方法被执行时会创建一个栈帧。方法被调用的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
    所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的。例如:int i = 0,虚拟机栈内存用4个字节来存储;引用型变量存储是一个地址引用,其大小也是固定。
    Class文件的常量池中存在有大量的符号引用,字节码中的方法调用指令就以指向常量池的引用作为参数。部分符号引用在类加载阶段(解析)转化为直接引用,这种转化为静态链接。部分符号引用在运行期间转化为直接引用,这种转化为动态链接
    Ocean river = new River();
    静态类型:编译期间确定的类型(Ocean)
    实际类型:运行期间确定的类型(River)

    该区域异常状况:

    • StackOverflowError:线程请求栈深度大于虚拟机所允许深度时抛出异常;
    • OutOfMemoryError:如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时抛出异常。
    // 虚拟机栈操作说明
    public int math() {
      int a = 1;
      int b = 2;
      int c = (a+b) * 10;
      return c;
    }
    

    javap -c -v test.class > test.txt反编译class文件后打开如下所示:

    虚拟机栈
    /**
      * -Xss228k,虚拟机栈大小为228k
      */
    public class Test { 
      private static int count = 0;
      public static void main(String[] args) { 
        Test test = new Test(); 
        test.test(); 
      }
    
       /**
        * 没有终止条件的递归调用 
        */
      private void test() {
        try {
          count++;
          test(); 
         } catch (Throwable e) {    
           //Exception已经捕获不了JVM抛出的StackOverflowError
          System.out.println("递归调用次数" + count); 
          e.printStackTrace();
        } 
      }
    }
    

    JVM抛出StackOverflowError表示线程请求的栈深度大于JVM所允许的深度。 对于单线程情况下,无论如何抛出的都是StackOverflowError。如果要抛出OOM异常,导致的原因是不断地在创建线程,直到将内存消耗殆尽。

    StackOverflowError

    -Xss设置每个线程的栈容量,此时如果栈内存越大,可以创建的线程数量少,就容易出现OOM;如果栈内存越小,可以创建的线程数量就多,就不容易出现OOM。要避免这种情况最好就是减少堆内存+方法区内存,或者适当减少栈内存。对于栈内存的配置,一般采用默认值1M,或者采用64位操作系统以及64位的JVM。


    剩余内存=JVM内存 - 堆内存 - 方法区内存。
    可以创建的线程数量 = 剩余内存 / 栈内存。

    3、本地方法栈(Native Method Stacks)

    本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一。

    该区域异常状况:
    该区域也会抛出StackOverflowError 和OutOfMemoryError异常。

    堆(Heap)

    堆是Java 虚拟机所管理的内存中最大的一块,被所有线程共享的一块内存区域。在虚拟机启动时创建,主要目的是存放对象实例。堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可。既可以实现成固定大小,也可以扩展的,通过-Xmx和-Xms 控制。

    随着JIT 编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换优化技术导致一些变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”。Java 堆是垃圾收集器管理的主要区域,如果从内存回收的角度看,现在收集器基本都是采用分代收集算法。


    Java 堆可分为
    年轻代老年代或者元空间

    年轻代分为
    Eden(伊甸园区)、Survivor(幸存区)。

    堆空间内存分配(默认情况下)
    年轻代 : 三分之一的堆空间
    老年代 : 三分之二的堆空间
    eden: 8/10 的年轻代空间
    survivorFrom : 1/10 的年轻代空间
    survivorTo : 1/10 的年轻代空间

    该区域异常状况:
    堆中没有内存完成实例分配并且堆无法再扩展,将抛出OutOfMemoryError 异常。

    • 新生代(Young Generation)

    一个类在这里产生、应用、最后被垃圾回收器收集结束生命。 当Eden区空间用完时,程序又需要创建对象,将在Eden区进行垃圾回收(Minor GC)。将Eden区中不再被其它对象引用的对象进行销毁。将Eden区中剩余的对象移到From Survivor区。若From Survivor区也满了,再对该区进行垃圾回收,然后移动到To Survivor区。From到To使用复制算法,转到15次(VM中对象头markOop中一个byte位0000从0-15,可以通过参数 -XX:MaxTenuringThreshold 设定)移到老年区。

    • 老年代(Old Generation)

    新生代经过多次GC仍然存在的对象移动到老年区。若老年代也满了,老年区将发生Major GC(也可以叫Full GC)。若Full GC之后依然无法进行对象的保存,就会抛出 OOM(OutOfMemoryError)异常。

    • 字符串常量池

    JDK1.7把字符串常量池从永久代中剥离出来,存放在堆空间中。

    命令行上执行如下命令,查看所有默认的JVM参数。

    java -XX:+PrintFlagsFinal -version
    

    输出两个有关键的参数

    [Global flags]
        uintx InitialSurvivorRatio     = 8         {product}
        uintx NewRatio                 = 2         {product}
        ... ...
    java version "1.8.0_91"
    Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
    Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
    

    参数解释

    参数 作用
    -XX:InitialSurvivorRatio 新生代Eden/Survivor空间的初始比例
    -XX:Newratio Old区 和 Yong区 的内存比例

    默认参数下,如果仅给出Eden区40M,求堆空间总大小?
    根据比例可以推算出,两个survivor区各5M,年轻代50M。老年代是年轻代的两倍,即100M。那么堆总大小就是150M。

    /**
    测试代码
    JVM参数配置
     -XX:MaxPermSize=10m
    -XX:PermSize=10m
    -Xms100m
    -Xmx100m
    -XX:-UseGCOverheadLimit
    */
    public class StringOomMock {
      public static void main(String[] args) {
        try {
          List<String> list = new ArrayList<String>();
          for (int i = 0; ; i++) {
            System.out.println(i);
            list.add(String.valueOf("String" + i++).intern());
          }
        } catch (java.lang.Exception e) {
          e.printStackTrace();
        }
      }
    }
    

    JDK1.6 环境下是永久代OOM

    Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
        at java.lang.String.intern(Native Method)
        at com.jd.im.StringOomMock.main(StringOomMock.java:17)
    

    JDK1.8运行结果同1.7一样,都是堆空间OOM。

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.lang.Integer.toString(Integer.java:403)
        at java.lang.String.valueOf(String.java:3099)
        at java.io.PrintStream.print(PrintStream.java:597)
        at java.io.PrintStream.println(PrintStream.java:736)
        at com.jd.im.StringOomMock.main(StringOomMock.java:16)
    

    方法区

    与Java 堆一样,各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java 虚拟机规范把方法区描述为堆的一个逻辑部分,别名叫非堆Non-Heap或永久代(Permanent Generation)。垃圾收集行为在这个区域比较少出现,该区内存回收主要针对常量池回收和对类卸载。

    image.png

    该区域异常状况:
    方法区无法满足内存分配需求时,将抛出OutOfMemoryError 异常。

    -XX:MaxPermSize=20M 方法区最大大小20M
    
    public class Test {
      public static void main(String[] args) { 
        List<String> list = new ArrayList<String>(); 
        int i = 0; 
        while (true) {
          list.add(String.valueOf(i++).intern());   
        //不断创建线程
        } 
      } 
    }
    
    • JDK6字符串常量池存在方法区,会抛出OutOfMemoryError:Permanent Space;

    • JDK7字符串常量池移到了Java堆中,上面的代码不会抛出OOM,若将堆内存改为20M则会抛出OutOfMemoryError:Java heap space;

    • JDK8元空间取代方法区这个概念,配置-XX:MaxPermSize没有任何意义,取代它的是-XX:MetaspaceSize-XX:MaxMetaspaceSize等。

    • 运行时常量池(Runtime Constant Pool)

    运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
    Java 虚拟机对Class 文件的每一部分的格式都有严格规定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、装载和执行。但对于运行时常量池,Java 虚拟机规范没有做任何细节的要求,运行期间也可能将新的常量放入池中。

    该区域异常状况:
    当常量池无法再申请到内存时,会抛出OutOfMemoryError 异常。

    • 元空间(Meta Space)

    在JDK1.8之后,元空间替代了永久代,它是对JVM规范中方法区的实现,区别在于元数据区不在虚拟机当中,而是用的本地内存,永久代在虚拟机当中,永久代逻辑结构上也属于堆,但是物理上不属于。
    元数据区和永久代本质上都是方法区的实现。方法区存放虚拟机加载的类信息,静态变量,常量等数据。

    为什么移除了永久代?
    参考官方解释http://openjdk.java.net/jeps/122
    移除永久代是为融合HotSpot与JRockit,因为JRockit没有永久代不需要配置。

    移除永久代
    JVM参数配置
    -XX:MetaspaceSize=8m 
    -XX:MaxMetaspaceSize=50m
    借助cglib框架生成新类。
    
    public class MetaSpaceOomMock {
        public static void main(String[] args) {
        ClassLoadingMXBean loadingBean = ManagementFactory.getClassLoadingMXBean();
        while (true) {
          Enhancer enhancer = new Enhancer();
          enhancer.setSuperclass(MetaSpaceOomMock.class);
          enhancer.setCallbackTypes(new Class[]{Dispatcher.class, 
              MethodInterceptor.class});
          enhancer.setCallbackFilter(new CallbackFilter() {
            @Override
            public int accept(Method method) {
              return 1;
            }
                    
        @Override
        public boolean equals(Object obj) {
          return super.equals(obj);
        }
        });
                
        Class clazz = enhancer.createClass();
        System.out.println(clazz.getName());
        //显示数量信息(共加载过的类型数目,当前有效的类型数目,已经被卸载的类型数目)
        System.out.println("total: " + loadingBean.getTotalLoadedClassCount());
        System.out.println("active: " + loadingBean.getLoadedClassCount());
        System.out.println("unloaded: " + loadingBean.getUnloadedClassCount());
        }
      }
    }
    

    该区域异常状况:
    JDK1.7报OOM的将是PermGen区域。
    JDK1.8报异常OutOfMemoryError: Metaspace。

    直接内存(Direct Memory)

    JDK1.4引入了NIO基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用Native函数库直接分配堆外内存。直接内存并不是虚拟机运行时数据区的一部分,也可能导致OutOfMemoryError 异常出现,所以我们放到这里一起讲解。
    本机直接内存的分配不会受到Java 堆大小的限制,肯定还是会受到本机总内存(包括RAM 及SWAP 区或者分页文件)大小及处理器寻址空间的限制。各个内存区域的总和大于物理内存限制,导致动态扩容时出现OutOfMemoryError异常。


    Java 栈、Java 堆、方法区 内存区域之间的关联关系

    Object obj = new Object();  
    

    Object obj这部分语义反映到Java 栈的本地变量表中,作为reference类型数据出现。
    new Object()这部分的语义将会反映到Java 堆中,形成一块存储了Object 类型所有实例数据值的结构化内存,这块内存的长度是不固定的。
    Java堆中包含能查找到此对象类型数据(对象类型、父类、实现的接口、方法等)的地址信息,这些类型数据则存储在方法区中。

    reference 类型在Java 虚拟机规范里面只规定了一个指向对象的引用,不同虚拟机实现的对象访问方式有所不同,主流的访问方式有两种:使用句柄直接指针

    句柄访问方式在Java堆中划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息。

    句柄访问方式的最大好处就是reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而reference 本身不需要被修改。

    句柄访问方式

    直接指针访问方式在Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,reference 中直接存储的就是对象地址。HotSpot默认方式。

    直接指针访问方式的最大好处就是速度更快,节省指针定位的时间开销,由于对象的访问在Java 中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。各种语言和框架使用句柄来访问的情况也十分常见。

    直接指针访问方式

    异常实例

    1、Java 堆溢出

    设置Java 堆的大小为20MB,不可扩展(将堆的最小值-Xms 参数与最大值-Xmx 参数设置为一样即可避免堆自动扩展),通过参数-XX:+HeapDump OnOutOfMemoryError 虚拟机在出现内存溢出异常时,Dump 出当前的内存堆转储快照以便事后进行分析。

    开发工具设置JVM
    Java堆溢出测试
    -verbose:gc -Xms20M -Xmx20M -XX:+PrintGCDetails
    
    public class HeapOutOfMemory {
        public static void main(String[] args) {
           List<TestCase> cases = new ArrayList<TestCase>();
           while (true) {
               cases.add(new TestCase());
           }
        }
    }
    
    2、java栈溢出
    Java栈溢出
    -Xss128k
    
    public class StackOverFlow {
      private int i ;
      public void plus() {
        i++;
        plus();
      }
    
      public static void main(String[] args) {
        StackOverFlow stackOverFlow = new StackOverFlow();
        try {
           stackOverFlow.plus();
        } catch (Exception e) {
          System.out.println("Exception:stack length:"+stackOverFlow.i);
          e.printStackTrace();
        } catch (Error e) {
               System.out.println("Error:stack length:"+stackOverFlow.i);
               e.printStackTrace();
        }
      }
    }
    
    3、常量池溢出
    常量池内存溢出
    -XX:PermSize=10M -XX:MaxPermSize=10M
    
    public class ConstantOutOfMemory {
      public static void main(String[] args)  throws Exception {
        try {
          List<String> strings = new ArrayList<String>();
          int i = 0;
          while (true) {
            strings.add(String.valueOf(i++).intern());
          }
        } catch (Exception e) {
          e.printStackTrace();
          throw e;
        }
      }
    }
    
    4、方法去溢出
     方法区溢出
     -XX:PermSize=10M -XX:MaxPermSize=10M
    
    public class MethodAreaOutOfMemory {
      public static void main(String[] args) {
        while(true) {
          Enhancer enhancer = new Enhancer();
          enhancer.setSuperclass(TestCase.class);
          enhancer.setUseCache(false);
          enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object arg0, Method arg1, Object[] arg2,
                MethodProxy arg3) throws Throwable {
                return arg3.invokeSuper(arg0, arg2);
            }
          });
    
          enhancer.create();
        }
      }
    }
    
    5、直接内存溢出
    直接内存溢出
    -Xmx20M -XX:MaxDirectMemorySize=10M
    
    public class DirectoryMemoryOutOfmemory {
      private static final int ONE_MB = 1024*1024;
      private static int count = 1;
      public static void main(String[] args) {
        try {
          Field field = Unsafe.class.getDeclaredField("theUnsafe");
          field.setAccessible(true);
          Unsafe unsafe = (Unsafe) field.get(**null**);
          while (true) {
            unsafe.allocateMemory(*ONE_MB*);
            count ++;
           }
         } catch (Exception e) {
          System.out.println("Exception:instance created "+count);
          e.printStackTrace();
        } catch (Error e) {
          System.out.println("Error:instance created "+*count*);
          e.printStackTrace();
        }
      }
    }
    

    逃逸分析:决定某些实例或者变量是否要在堆中进行分配,如果开启了逃逸分析,即可将这些变量直接在栈上进行分配,而非堆上进行分配。这些变量的指针可以被全局所引用,或者其其它线程所引用。

    内存分配担保机制:当在新生代无法分配内存的时候,把新生代的对象转移到老生代,然后把新对象放入腾空的新生代。

    JVM 内存参数说明

    高并发注意停顿时间,吞吐量是垃圾收集时间和总用户代码执行时间占比。
    最低延时考虑FullGC最长停顿时间,平均停顿时间,吞吐量考虑FullGC次数。

    image
    • -Xms:设置堆的最小值(初始值);
    • -Xmx:设置堆的最大值;
    • -Xmn:设置年轻代的大小,表示NewSize=MaxNewSize=Xmn,一般建议该参数来设置新生代大小,避免新生代扩容,优先级低于-XX:NewSize-XX:MaxNewSize参数;
    • -XX:NewSize:设置年轻代最小值(初始值);
    • -XX:MaxNewSize:设置年轻代最大值;
    • -XX:NewRatio:设置年轻代和年老代的比值。如:为3,表示年轻代:年老代=1:3,年轻代占整个年轻代年老代和的1/4;
    • -XX:SurvivorRatio:设置年轻代中Eden区与两个Survivor区的比值。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5;
    • -XX:PermSize: 设置方法区初始大小(JDK1.7及以前);
    • -XX:MaxPermSize: 设置方法区最大大小(JDK1.7及以前);
    • -XX:MetaspaceSize: 设置元数据区初始值(JDK1.8及以后);
    • -XX:MaxMetaspaceSize:设置 元数据区最大值(JDK1.8及以后);
    • -XX:MaxDirectMemorySize:设置直接内存大小,默认与堆内存最大值一样(-Xmx);
    • -Xss:设置一个线程栈的大小,JDK5.0 以后每个线程 Java 栈大小为 1M,以前每个线程堆栈大小为 256K。根据应用的线程所需内存大小进行调整。在相同物理内存下,减 小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成。
    • -XX:MaxTenuringThreshold:设置垃圾最大年龄。如果设置为 0 的话,则年轻代对象不经 过 Survivor 区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行多次复制,这样可以增加对象再年轻代 的存活时间,增加在年轻代即被回收的概率。
    • -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/heap/dump:设置当JVM发生OOM时,自动生成DUMP文件,/path/heap/dump表示文件路径;
    • -verbose:gc:表示输出GC的详细情况;

    - 和 -X、 -XX的区别

    • -:标准参数,所有的JVM实现都必须实现这些参数的功能,而且向后兼容;
    • -X:非标准参数,默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;
    • -XX:非稳定参数,此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用;

    示例

    JDK 1.7: set JAVA_OPTS=-Xms1024m -Xmx1024m -Xss512k -XX:PermSize=128m -XX:MaxPermSize=256m -XX:NewSize=256m -XX:MaxNewSize=256m

    JDK1.8 : set JAVA_OPTS=-Xms1024m -Xmx1024m -Xss512k -XX:MetaspaceSize=128m -XX:MAXMetaspaceSize=256m -XX:NewSize=256m -XX:MaxNewSize=256m

    查看JVM运行时参数

    • -XX:+PrintFlagsInitial 查看初始值
    • -XX:+PrintFlagsFinal 查看最终值(初始值可能被修改掉)
    • -XX:+UnlockExperimentalVMOptions 解锁实验性参数
    • -XX:+UnlockDiagnosticVMOptions 解锁诊断参数
    • -XX:+PrintCommandLineFlags 打印命令行参数
    • -XX:+PrintGCDetails 查看GC信息

    示例

    java -XX:+PrintFlagsInitial -version
    
    

    输出结果:

    D:\workspace\etc\etc-credit-card>java -XX:+PrintFlagsInitial -version
    [Global flags]
        uintx AdaptiveSizeDecrementScaleFactor          = 4       {product}
        uintx AdaptiveSizeMajorGCDecayTimeScale         = 10      {product}
        uintx AdaptiveSizePausePolicy                   = 0       {product}
        uintx AdaptiveSizePolicyCollectionCostMargin    = 50      {product}
        uintx AdaptiveSizePolicyInitializingSteps       = 20      {product}
        uintx AdaptiveSizePolicyOutputInterval          = 0       {product}
        uintx AdaptiveSizePolicyWeight                  = 10      {product}
        uintx AdaptiveSizeThroughPutPolicy              = 0       {product}
        uintx AdaptiveTimeWeight                        = 25      {product}
         bool AdjustConcurrency                         = false   {product}
         bool AggressiveOpts                            = false   {product}
         intx AliasLevel                                = 3       {C2 product}
         bool AlignVector                               = true    {C2 product}
         bool UseLargePagesIndividualAllocation        := false   {pd product}
    ...
    
    

    将结果输出到文本:

    java -XX:+PrintFlagsInitial -version > flag.txt
    
    

    =表示默认值; :=表示被用户或JVM修改后的值

    image.png

    相关文章

      网友评论

          本文标题:JVM结构及配置

          本文链接:https://www.haomeiwen.com/subject/qeyhqhtx.html