jvm

【深入解析JVM】--最新版

JVM

jdk、jre、jvm的关系

JDK:是Java开发工具包,是Sun Microsystems针对Java开发员的产品。
JDK中包含JRE,在JDK的安装目录下有一个名为jre的目录,里面有两个文件夹bin和lib,在这里可以认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和 lib和起来就称为jre。
JRE:是java程序的运行环境,它包含JVM。
三者的关系:JDK(JRE(JVM))

什么是JVM

可以简单的理解为:就是运行编译好的java文件生成 的.class文件,并且解析为当前运行系统所对应的指令。

1. Java程序的跨平台特性主要是指字节码文件可以在任何具有Java虚拟机的计算机或者电子设备上运行,Java虚拟机中的Java解释器负责将字节码文件解释成为特定的机器码进行运行,

  1. 粗略分来,JVM的内部体系结构分为三部分,分别是:类装载器(ClassLoader)子系统,运行时数据区,和执行引擎。

JVM 的位置在哪里?

在这里插入图片描述

JVM架构图

在这里插入图片描述

一、类装载子系统

1.通过一个类的全限定明获取定义此类的二进制字节流;

2.将这个字节流所代表的的静态存储结构转化为方法区的运行时数据;

3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

类加载器分类

  1. 引导类加载器[BootStrapClassLoader]:负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。
  2. 扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库。
  3. 应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库。

JVM通过双亲委派模型进行类的加载,当然我们也可以通过继承java.lang.ClassLoader实现自定义的类加载器。

链接模块

在这里插入图片描述

  1. 验证(Verify)

    1. 目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全。

    2. 主要包括四种验证,文件格式验证,源数据验证,字节码验证,符号引用验证。

  2. 准备(Prepare)

    1. 为类变量分配内存并且设置该类变量的默认初始值,即零值;
    2. 这里不包含用final修饰的static,因为final在编译的时候就会分配了,准备阶段会显式初始化;
    3. 之类不会为实例变量分配初始化,类变量会分配在方法去中,而实例变量是会随着对象一起分配到java堆中。
  3. 解析(Resolve)

    1. 将常量池内的符号引用转换为直接引用的过程。

初始化模块

  1. init(构造器初始化)

默认每个类都有

  1. cinit(静态变量初始化)

    当在代码中有静态值,或者静态代码块之类,会自动创建cinit初始化方法

①.双亲委派机制(重要)

在这里插入图片描述

每个类都默认有一个父类Object ,当在加载类时,会自动向上委托,由父类的类加载器进行加载,如果父类还存在其他父类那么会以此类推(类似递归)向上委托,最终由父类的,启动类加载器进行加载,如果父类的加载子系统,不能加载子类,那么就会向下委托,由子类本身加载,这就是双亲委派机制。

优点:

  1. 避免了重复的加载

  2. 保护程序的安全,防止API被串改(如手动写java.lang包String类)

程序计数器

简介

  1. 程序计数器也称PC寄存器 (PC Register)
  2. PC寄存器是用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎读取下一条。
  3. 它是一个很小的空间,几乎可以忽略不计。也是运行速度最快的存储区域。

举例说明:

在这里插入图片描述

声明:简要理解也就是因为在运行时CPU需要不停的切换个线程,这时候就需要PC寄存器来记录当前线程运行到哪个位置了,即将运行哪里给记录下来,等待下次切换回后进行调用。

二、栈

详情结构图

在这里插入图片描述

虚拟机栈的特点

  1. 内存小,跨平台性,可以少量存储一些变量,内存地址,用来管理Java方法的调用。
  2. 栈也就是方法,一个线程就是一个栈,所以说是线程安全的,是线程私有的—>声明周期与线程同步
  3. 先进后出(执行完就出)栈帧相当于执行方法,调用完就出
  4. 因为栈比较小,栈不存在GC问题(OOM)

声明:

一个线程就是一个栈 栈中包含栈帧: 栈帧也就是方法---栈帧中包 含变量表-->变量表包含 变量槽Slot(也就是索引)--->double long因为是8个字节64位占用2个Slot 32位一个,按照加载变量的顺序来分配,索引槽,this(静态是没有this变量槽)默认第一个0,只要不是静态默认都有this 操作数栈:在变量进入变量表时都会经过操作数栈 动态链接:引用调用常量池中的数据 方法的调用:静态链接:早期绑定---在编译时就确定了调用的方法,如static final private 修饰的方法 动态链接:晚期绑定:在运行时才确定了方法 比如接口

栈异常

StackOverflowError:当栈分配的空间不足时,那么会出现—>通常是递归时

设置栈的大小

-Xss:如-Xss128k

本地方法栈(了解)

  1. 本地方法栈也是私有的。
  2. 本地方法栈用于管理本地方法的调用。
  3. 允许被实现成固定或者是可动态扩展的内存大小。
  4. 本地方法是用C语言实现的

声明:粗略的可以理解为本地方法栈就是使用java可以调用本地方法(C语言编写)简介的操作内存等。



三、堆

在这里插入图片描述

简介

  1. 堆在运行时数据区占用比较大的一部分
  2. 通常来说new对象都是放在堆中的。
  3. 堆中又分为新生代、老年代。
  4. 内存默认比例分配1:2
  5. 堆中存放字符串常量池(后有详解)

代码简单优化建议

开发中能使用局部变量的,就不要使用全局

①新生代

简介

  1. 新生代使用了复制算法
  2. 新生代为gc的重点对象,经官方测试70%对象都生命周期都会在新生代中完结
  3. 新生代又分为了eden、survivor1、survivor2
  4. 内存比例分默认为:8:1:1;j8默认开通自适配比例可能有所变化6:2:2
  5. 新生代收集器:Minor GC/Young GC

eden(新生区)

​ 当初始加载对象时会进入新生区

survivor(幸存区)

  1. 幸存区又分为from 和 to —谁为空谁为to ,始终都会有一个区域为空。

  2. 幸存区不会主动进行垃圾回收,只会eden回收时才会附带进行gc

  3. 当在幸存区中的阈值达到了15后(默认15可修改)会自动进入老年代

​ 当新生区(eden)出现了内存不足时,会进行YGC,那么会将没有指针的对象回收,还有指针引向的对象放入survivor1或者survivor2区域中,eden清空,数据放入一个survivor中。—当第二次进行gc那么会将eden数据放入另一个空的survivor中,并且将当前survivor中有效数据,放入空的survivor中,一次类推。

TLAB(快速分配策略)

​ 由于堆中的空间都是共享的,所以存在线程安全的问题,这时候就出现了TLAB

​ 缓冲区的线程私有的 TLAB ,保证了安全性,是在eden 中只占1%内存可能成功也可能失败,快速分配策略

声明

在一个对象进入内存时 会进入eden,如果满了(YGC进行回收没有引用的,如果还有引用的)会放入s1或者s0这就涉及到to from哪个为空就是to,(下次eden再次满了会将有数据的【举例s1】中的数据放入s0,并且进行迭代版本)以此类推,当某个对象迭代阈值的次数达到默认15此后,(当然也会有特殊的优化:如当survivor区域中相同年龄的内存总和大于survivor的一半内存,会将大于等于平均年龄的对象提前放入老年代)会放入老年代 关于YGC 全程(YoungGC) 也可以为(Minor GC) s1,0是不会有单独的gc回收只会被动的依赖于eden的gc当eden进行gc时会自动回收s1,s0

②老年代

特性

  1. 较大的对象数据会放入老年代
  2. 老年代的数据都是相对于持久的不会频繁的gc
  3. (MajorGC / Old GC) 在进行majorgc时会至少进行一次minorGc ,而且majorgc的效率是比minorGc 慢10倍的
  4. 老年代收集器:MajorGC / Old GC 要区分与Full GC

Full GC :是进行整堆的回收

③逃逸分析

什么是逃逸?

​ 也就是如果在方法内创建对象,并且return进行传出,或者赋值到外部的变量,那么就进行了逃逸。

​ -XX:+DoEscapeAnalysis (JDK1.8之后默认开启)

​ -XX:+DoEscapeAnalysis(关闭)

逃逸分析包括以下

栈上分配

也就是将对象直接分配到栈上,跟随栈的消亡而消亡,减少了gc(栈中没有gc),提高了性能、速度。

同步省略

因为是每个栈独有的,一个栈也就是一个线程所以不存在同步安全的问题。

分离对象或者标量替换

扩充:一个类代表一个:聚合量,标量是无法分析的最小数据,聚合量可以分析为标量,也就是分析属性

也就是当加载一个pojo类时,不会创建对象而是,标量替换进行分析成一个个小的属性,减少了内存,提高了性能。

但是基于hotSpot 虚拟机这项技术并不成熟,因为还需要进行判断是否 属于逃逸,如果没有逃逸,可能会浪费了判断的时间等一些问题。
但是最后标量替换还是引用到了hotSpot虚拟机中

所以问题—所有的对象都是存储在堆空间中么?

回答:是的


四、方法区

简介

  1. 方法区在Hotspot中又称永久代、元空间(非堆)[这里的永久代、元空间在JVM虚拟机规范中是不等价的]
  2. 在jdk1.7(包含7)以前称为永久代,并且方法区是在JVM中
  3. 在jdk1.8以后称为元空间,并将方法区移除JVM的约束
  4. 使用垃圾收集器:FullGC
  5. 其中内含了常量池、域(Field)信息、已装载类信息、方法信息、JIT代码缓存
  6. 方法区是直接内存(也就是直接分配在内存上的)

存储迁移过程

在这里插入图片描述

为什么要移除堆空间?

  1. 顾名思义(永久代)即经常不回被回收的,跟随电脑的内存进行扩展,可以减少gc,提高性能,并减少了内存溢出的风险。

常量池

常量池包含各种字面量和对类型、域和方法的符号引用。

参数设置

初始值 -XX:MetaspaceSize=100m

最大值 -XX:MaxMetaspaceSize=100m

建议不要随意修改设置,因可以跟随本地内存变化而进行扩充变化


  • 经过以上学习类装载子系统、栈、堆、方法区、可延伸面试题:

对象在JVM中是怎么存储的?

在这里插入图片描述

​ 答:创建对象的步骤有六种

  1. 判断对象对应的类是否经过类加载子系统加载过
  2. 为对象分配内存
  3. 处理并发安全的问题(TLAB)(CAS)
  4. 初始化分配到空间
  5. 设置对象的对象头
  6. 执行init方法进行初始化

对象头里面有什么?

  1. 类型指针—>指向类源数据,确定对象所属类型
  2. 运行时元数据

声明:如果是数组,还需要记录数组的长度。

图解流程–以上两问

在这里插入图片描述

五、对象访问定位

句柄访问

​ 通俗来说,就是在栈中的动态链表存储着变量对象在堆空间的引用地址,再开辟一个空间作为中间商来链接堆中的地址,那么栈中的引用直接记录中间商的地址就可以了,当堆空间地址变化了,也不需要进行变化处理。

直接指针(Hotspot使用)

​ 也就指栈中的动态链表存储着变量对象在堆空间的引用地址,是直连的方式,当堆中的地址变化,那么栈中也需要进行变化。


六、执行引擎

  1. java代码被编译成字节码的时候可以称为前端编译器,在进行第二次编译为本地系统所能识别的指令称后端编译器
  2. 执行引擎也称后端编译器

执行的流程

在这里插入图片描述

什么是解释器?

​ 也就是将字节码文件翻译为对应平台的机器指令)(低效)运行流程:直接 翻译,直接运行,也就是一行代码一行代码进行编译

什么是编译器?

​ 将源代码直接编译为对应本地平台的机器指令(JIT编译器—》及时编译器)运行流程:先翻译,翻译后再执行,响应时间慢,但是响应过后非常快。解析热点代码(也就是执行频率高的)后会进行缓存起来,下次调用就直接调用的机器指令不需要再次解析,效率高

为什么java是半编译半解释语言?

因为hotspot虚拟机 既有编译器又有解释器互相结合。效率更高

图解

在这里插入图片描述

七、垃圾回收器

①概念。

什么是垃圾?

答:垃圾就是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾。

什么是引用计数算法?(java没有引用这个算法,python使用)

简单理解来说就是对没一个对象都保存一个整型的计数器,当有人引用时+1 ,不引用了-1,到0时那么就被认定可回收

优点:简单,高效

主要缺点:无法处理循环引用(存在内存泄漏)

吞吐量—延迟性

“程序运行的时间占用总时长的比例” 也就是程序时间/(程序时间+垃圾回收时间) 占用率越高越好 ,一般高吞吐量的那么都是不需要太大交互的(例如)

延迟性也就是回收的时候SWT停顿的时间

两个之间是相对比的,不能同时兼顾极致。

STW

Stop The word :简称STW 指的是在GC垃圾回收事件时,会产生成语的停顿(会让用户线程进行停顿,进行GC快照)

System.gc()

调用gc(使用的是FullGC) 但是不一定能够及时调到 底层调用的是(Runtime.getRuntime.gc());

一般不会调用,只有在调优测试可能会调用

②GC Roots

可达性分析(或 跟搜索算法、追踪性垃圾收集)java使用

关联GC Roots的就是可达性的,对象不应该被回收,不关联的可能会进行回收,看“图文”是否关联

图文理解

在这里插入图片描述

哪些对象是关联GC Roots的?

  1. 虚拟机栈中的引用对象

    如:各个线程被调用的方法中使用到的参数、局部变量等

  2. 本地方法栈内JNI(通常说本地方法)引用的对象

  3. 方法区中类静态属性引用的对象

  4. 方法区中常量引用的对象

  5. 所有被同步锁Synchronized只有的对象

  6. java虚拟机内部的引用

finalization方法

  1. finalization方法是父类Object中的一个方法,可以被重载
  2. finalization方法一般不会去主动调用,因为也没有意义,默认是空的。
  3. finalization方法是在gc时会自动调用,并且只会触及一次

由于方法的存在,可将虚拟机中的对象分为三种

  1. 可触及的:冲根节点开始,可以到达这个对象
  2. 可复活的:对象的的引用断开,开始回收,调用了finalization方法又重新引用了。
  3. 不可触及的:也就是对象的引用断开了,并且已经调用过了finalization方法了,只能被回收了

具体过程

内存泄露

什么是内存泄露?

答:java中对象已经不再使用,但是gc还没办法进行回收

举例

  1. 单例模式

单例的生命周期和应用程序是一样长的,所以单例程序中,如果持有对外部对象的引用的话,那么这个外部对象是不能被回收的,则会导致内.存泄漏的产生。

  1. 一些提空了close方法关闭资源而没有进行关闭的

数据库连接(dataSourse . getConnection()),网络连接(socket)和io连接必须手动close,否则是不能被回收的。

算法介绍

分带收集

也就是将内存分代进行收集

​ 如:在java堆分成新生代(eden s1 s2[复制算法](不会产生内存的碎片化))老年代(标记-清除算法(会参数内存的碎片化,再次存储是空闲列表方式);标记-整理算法(不会产生碎片化,再次存储时是指针碰撞的方式进行存储))

增量收集算法

一次回收一点,减少延迟性,提高吞吐量

分区算法

就是将堆分为一块一块的小区间region,都相对于是独立的。好处是可以控制一次回收多少个小区间

复制算法

在新生代中使用的就是复制算法。

优点:不会产生内存的碎片化,运行速度快。

缺点:如果在不确保是大量垃圾的情况下,可能会导致时间、资源的浪费。

标记–清除算法

将需要进行回收的垃圾进行标记,当进行gc时进行清除(扩充:这里的清除其实并不是在空间中给remove而是标记到空闲列表等待下次进行存储的话直接覆盖),再次存储是空闲列表方式。

优点:相对比运行速度快

缺点:内存有碎片化,当存储大容量的对象时可能会导致OOM

标记–整理算法

将需要进行回收的垃圾进行标记,当进行gc时进行清除,清除后,会对内存进行整理不会产生碎片化,再次存储时是指针碰撞的方式进行存储。

优点:不会有碎片化

缺点:运行速度较慢

安全点

什么时候都可以GC么?

不是什么时候都可以进行直接GC的 只有在制定的安全点才可以

如何发生GC时,检查所有的线程都跑到了安全点?

抢先式中断:(目前是没有任何商用虚拟机采用)

首先中断用户线程,查看是否到了安全点,如果没有那么继续进行跑(目前是没有任何虚拟机采用)

主动式中断:(大多采用)

设置一个中断的标志,当线程都运行到了主动轮询的标志时,如果中断标志为true,那么进行挂起。

引用—偏门高频面试

引用分为:强引用、软、弱、虚引用分别是依次递减

引用:当内存空足够时,则保存在内存空间中,如果垃圾回收后,内存空间不够时,那么就抛弃这些对象

在java中有一个抽象类分别可以实现软、弱、虚

在这里插入图片描述

强引用

平常开发99%都是强引用

​ 如:String str = new String()

软引用

使用场景:缓存居多

特点:当内存不够时,那么进行回收,内存够那么继续存在

弱引用

特点:每次gc都会进行直接回收

HotSpot落地GC

在这里插入图片描述

Serial—Serial old

单核gc处理,串行,针对于新生代的垃圾回收器,采用了复制算法,串行回收----同和Serial old垃圾回收器(收集老年代的垃圾回收器)采用的标记-压缩算法 优点:在单线程的情况下,简单高效,一般使用在单cpu精简的情况下 主动配置使用SerialGC:-XX:+UseSerialGC

ParNew—CMS || SerialOld

并行回收器,新生代。采用的算法同上。 老年代使用的CMS GC (J14已经移除了)或者SerialOld(J9不再关联) 采用的算法同上;最新版本已经不能使用了 主动配置:(必须要在低版本的如J14以下或者J9以下的版本)-XX:+UseParNewGC

Parallel—Parallel old

并行回收器,新生代, 效率其实和上面差不多,但是比上面的会更好一点,算法同上,吞吐量更有限可控,自适应调解策略(也就是可以调解吞吐量优先还是 暂停时间优先)---组合Parallel old jdk1.8默认使用

CMS

在老年代—是一款并发的垃圾处理器–低延迟,采用的是标记-清除算法

CMS优点:低延迟,并发

缺点:会产出内存碎片,并且在高峰内存的情况下可能会导致内存不够用那么,就直接执行FullGC回降级使用替补的GC SerialOld)(串行)
无法处理浮动的垃圾
对cpu的资源敏感

为什么说这个是低延迟的?

工作原理:用户线程—》初始标记(STW)GC—》继续用户线程并且并发标记—》重新标记(STW)因为在后面用户线程又进行了使用所有需要重新标记下垃圾—并发清理;

图解原理

在这里插入图片描述

G1

特点

  1. 现在主流G1垃圾回收
  2. 主要应用在服务器端,针对于大内存,多处理器
    官方说明:延迟可控的情况下尽可能获得更高的吞吐量,所以才担任了“全功能收集器的”重任
    采用的区域(region)分代化(分区算法)
    兼具了并行,并发,分区,空间整合,可预测的停顿时间模型
    Region之间是复制算法,,但整体上可以看做是标记-整理
  3. ​ 缺点:内存占用比cms更高
  4. g1在存储大对象时会专门划分一个区域H区 区别于老年代
  5. 设置H区域的原因 如果创建了一个大对象并且 生命周期很短,放在老年代中,那么就会造成了内存的泄露

声明:G1垃圾回收是分成一块一块Region进行回收,每次回收并不是全部垃圾都进行回收,而是根据延迟时间,来定制收取占比率较高的region

记忆集RememberdSet

声明:也就是在因为在g1是进行分区的垃圾回收,所以可能会出现一个区域中使用的对象,在另一个区域中也有引用,那么在回收时就需要全部遍历一遍回收,显然效率太低

所以有了记忆集:也就是记录了当前年轻代区中的对象在老年代区中的引用位置(如果有那么在ygc时不会回收掉);

G1垃圾回收过程

G1垃圾回收主要包括三个环节:

年轻代 YGC

老年代并发标记过程(Concurrent Marking)

混合回收 (Mixed GC)

在这里插入图片描述

G1回收过程具体

在这里插入图片描述

经典垃圾回收器大致流程图

在这里插入图片描述

七种垃圾回收器总结

在这里插入图片描述

参数总结

官方参数说明

测试堆空间常用的jvm参数:
* -XX:+PrintFlagsInitial :查看所有的参数的默认初始算
* -XX:+PrintFlagsFinal :查看所有的参数的最终值(可能会存在修改,不再是初始值)
* -Xms: 初始堆空间内存 (默认为物理内存的1/64)
* -Xmx: 最大堆空间内存(默认为物理内存的1/4)
* -Xmn: 设置新生代的大小。(初始值及最大值)
* -XX:NewRatio: 配置新生代与老年代在堆结构的占比   
* -XX:SurvivorRatio: 设置新生代中Eden和se/S1空间的比例
* -XX:MaxTenuringThreshold: 设置新生代垃圾的最大年龄 
* -XX:+PrintGCDetails: 输出详细的GC处理日志
* 打印gc简要信息:-XX:+PrintGC
* -XX:HandlePromotionFailure: 是否设置空间分配担保

//查看默认的垃圾收集器
-XX: +PrintCommandLineFlags:查看命令行相关参数(包含使用的垃圾收集器)
使用命令行指令: jinfo -flag 相关垃圾回收器参数 进程ID
(需要配合jps进程号来使用)
//例:jinfo -flag UseParallelGC 17948

//Parallel 参数
-XX:+UseParalle1Gc 手动指定年轻代使用Parallel并行收集器执行内存回收
-XX:+UseParallel0ldGc 手动指定老年代都是使用并行回收收集器。(默认两个互相激活,开启一个就可以)
-XX: ParallelccThreads 设置年轻代并行收集器的线程数。- -般地,最好与CPU数量相等,以避免过多的线程数影响垃圾收集性能。
-XX:MaxGCPauseMillis 设置垃圾收集器最大停顿时间(即STw的时间)。单位ms。 ##谨慎使用
-XX:GCTimeRatio垃圾收集时间占总时间的比例(=1/(N+1))。用于衡量吞吐量的大小。
-XX: +UseAdaptiveSizePolicy 设置Parallel Scavenge收集器具有自适应调节策略

//CMS 参数设置
-XX: +UseConcMarkSweepGC 手动指定使用CMS收集器执行内存回收任务。
-XX: +UseCMSCompactAtFullCollection用于指定在执行完FullGC后对内存空间进行压缩整理,以此避免内存碎片的产生。不过由于内存压缩整理过程无法并发执行,所带来的问题就是停顿时间变得更长了。
-XX: CMSFullGCsBeforeCompaction 设置在执行多少次Full GC后对内存空间进行压缩整理。
-XX: ParallelCMSThreads设置CMS的线程数量。
-XX:CMS1ni tiatingOccupanyFraction 设置堆内存使用率的阈值, 一旦达到该阈值,便开始进行回收。
➢JDK5及以前版本的默认值为68 ,即当老年代的空间使用率达到68号时,会执行一次CMS回收。JDK6及 以上版本默认值为92%

//G1 参数设置
-XX: +UseG1GC 手动指定使用G1收集器执行内存回收任务。
-XX:G1HeapRegionSize设置每个Region的大小。值是2的幂,范围是1MB到32MB之间,目标是根据最小的Java堆大小划分出约2048个区域。默认是堆内存的1/2000.
-XX :MaxGCPauseMillis 设置期望达到的最大Gc停顿时间指标(JVM会尽力实现,但不保证达到)。默认值是200ms
-XX: ParallelGCThread 设置STw工作线程数的值。最多设置为8
-XX: ConcGCThreads 设置并发标记的线程数。将n设置为并行垃圾回收线程数(ParallelGCThreads)1/4左右。
-XX:InitiatingHeapOccupancyPercent 设 置触发并发GC周期的Java堆占用率阈值。超过此值,就触发GC。默认值是45

//日志的查看
-XX:+PrintGC 输出Gc日志。类似: -verbose:gc
-XX:+PrintGCDetails 输出Gc的详细日志
-XX:+PrintGCTimeStamps 输出Gc的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如2013-05-04T21 :53:59.234+0800)
-XX:+PrintHeapAtGC 在进行Gc的前后打印出堆的信息
-Xloggc:.. /logs/gc.log日志文件的输出路径

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值