为什么要了解垃圾回收与内存分配?

为什么要了解垃圾回收与内存分配?

当需要排查出各种内存溢出,内存泄露问题时,当垃圾收集成为系统达到更高并发的瓶颈时,就知道了解垃圾回收与内存分配的策略。

垃圾回收算法

垃圾回收要解决的首要问题,就是找出哪些对象有用,哪些对象无用,无用的对象就要被回收。比较常见的对象标明算法有引用计数器算法 和 可达性分析算法。

  • 引用计数器:原理是记录被引用了多少次,增加一次引用,值增1,减少引用,值减1,如果引用为0,说明对象可以被回收。该算法的效率高,原理也简单,但是不能解决 对象之间循环引用 的问题。
  • 可达性分析:通过一些 称为 GC Roots 对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为 引用链 Reference Chain。当一个对象到GC Roots 没有任何引用链 相连时,则说明这个对象不可达,可以被回收。
常见的 GC Roots:

1 虚拟机栈(栈帧中本地变量表)中引用的对象, 如每个线程被调用的方法堆栈中使用的参数,局部变量。
2 方法区中 静态属性 引用的对象,Java 类引用的静态变量。
3 方法区中 常量 引用的对象,字符串常量池中的引用。
4 Java 虚机内部的的引用,如基本数据类型对应的 Class 对象,系统类加载器。
5 所有被同步锁持有的对象,如: Synchronized 修饰的对象。
除了上面这些固定的 GC Roots 对象之外,根据用户所选择的垃圾器以及当前回收的内存区域的不同,其他对象可“临时性”的加入 GC Roots集合。比如在分代收集和局部回收时,如果只针对新生代进行垃圾回收时,必须考虑到新生代的对象很有可能被 Java 堆的其他区域的对象所引用,这时候就需要考虑将这些对象加入到 GC Root集合中去。
目前比较新的几款垃圾收集器都具备了局部回收的特征。

再谈引用:

Java1.2之前对引用的定义很传统:

如果reference 类型的数据是另一块内存起始地址,就称该 reference 数据代表某块内存,某个对象的引用。

但是,对于一些特殊情况,这种定义就过于生硬了,比如在内存足够的情况下,保留一些对象,在内存紧张的情况下,回收一些对象。很多系统缓存都符合这样的情况。
在 Java1.2之后,对引用进行了扩充,将引用可以分为 强引用 Strongly Reference软引用 Soft Reference弱引用 Week Reference虚引用 Phantom Reference四类:

  1. 强引用: 就是通过 new 关键字 创建出来的对象。不管什么时候,只要引用关系还存在,虚拟机就不会回收这类对象。
  2. 软引用: 描述一些还有用,但非必须的对象。虚拟机在内存溢出之前,会回收这类对象,进行二次回收。如果回收之后,内存还是紧张,就抛出内存溢出异常。
    软引用可用来实现内存敏感的高速缓存。
    a. 如果一个对象只具有软引用,则内存空间充足时,垃圾回收器就不会回收它;
    b. 如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。
    当内存不足时,JVM首先将软引用中的对象引用置为null,然后通知垃圾回收器进行回收:
  if(JVM内存不足) {
      // 将软引用中的对象引用置为null
      str = null;
      // 通知垃圾回收器进行回收
      System.gc();
  }

也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软引用对象,而且虚拟机会尽可能优先回收长时间闲置不用的软引用对象。对那些刚构建的或刚使用过的 "较新的"软对象会被虚拟机尽可能保留

  1. 弱引用: 描述的也是一些有用,但非必须的对象。只是这些对象的强度比软引用更软一些。被弱引用引用的对象,只能活到下次垃圾回收之前。垃圾收集器不管内存是否足够,都会回收这类对象。
    弱引用软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。`

  2. 虚引用: 描述最弱的一种引用关系。对象是否有虚引用存在,完全不会对生存时间有影响,也无法通过虚引用来引用一个对象。使用虚引用的唯一目的是:在对象被垃圾回收时收到一个系统通知。

    继承关系:
    Reference
    |
————————————————————————————————————————————————————————
    |					|					|			
    SoftReference		WeakReference	PhantomReference.
对象的生存还是死亡

通过可达性分析算法 被标记为 回收的 对象,并不是非死不可。要真正的标明一个对象死亡,至少要经历2次标记过程:

  1. 如果对象在经过可达性分析之后,发现没有GC Roots引用与之相连,那会触发第一次标记并进行一次筛选,筛选的条件就是,对象 是否有必要执行 finalize() 方法,
    a. 如果对象没有覆盖 finalize() 方法 或者 对象的 finalize() 方法 已经被执行,2种情况都被视为 ”没有必要执行“。
    b. 如果对象被判定为 有必要执行 finalize 则把对象放到一个 F-Queue队列中,稍后由虚拟机建立的一个低优先级的Finalizer线程去执行,但并不保证他会等待 finalize方法执行结束。之所以会这样是因为,如果finalize() 方法中耗时操作,则会影响F-Queue队列,从而影响整个GC。因此finalize()方法是对象逃脱死亡的最后一次机会。

稍后,GC将对F-Queue中的对象进行二次小规模标记,如果这时,对象的引用 与 其他对象建立的关联,那么,对象就被移出F-Queue队列,否则,对象将会被真回收。假如某对象在复活之后又被标记为 等待回收,那么对象的finalize方法将不会被再次调用,一个对象的finalize()方法只会在垃圾收集之前被调用一次,此时对象会直接回收。

方法区的回收

方法区在HotSpot中也被称为永久代,JDK8之后就没有永久代的概念来,取而代之的是元空间。对永久代的回收,主要针对 废弃常量无用的类。方法区的GC非常的不频繁。
假如对java字符串常量进行垃圾回收,条件是虚拟机中没有任何字符串的值是java,即没有任何引用指向java,此进行垃圾回收则java字符串将会被清理出常量池。
如果要回收一个类,需要满足以下3个条件:

  1. 类的所对象的 java.lang.Class 对象被回收。
  2. 类的所有 Object 都被回收。
  3. 类的ClassLoader被回收,此条件一般是经过设计的可替换类加载器的场景,否则这容易达成。
    虚拟机允许满足上面3个条件的类被回收,而这仅仅是被允许,并不是与对象一样,没有引用就必然会被回收。
    hotSpop 虚拟机提供了-Xnoclassgc参数控制 是否要对类进行回收。还可以用-verbose:class-XX:TraceClassLoading-XX:TraceClassUnLoading查看类的加载和卸载信息。
垃圾收集算法

如何判断对象已经死的角度出发,垃圾收集算法可以分为引用计算垃圾收集 Reference Counting GC追踪式垃圾收集两大类,又称为直接垃圾收集间接垃圾收集

分代垃圾收集

分代垃圾收集的思相建立在一套符合大多数程序运行实际情况的经验法是上,它建立在两个假说之上:

  1. 弱分代思想(Weak Generational Hypothesis):绝大多数对象都是朝生夕死的。
  2. 强分代思想(Strong Generational Hypothesis):熬过越多次垃圾收集过程的对象就越难以消亡。

这两个假说奠定了多个垃圾收集器的设置原则:
收集器应该将 Java 堆划分成不同的区域,然后将回收对象根据年龄分配到不同的区域中。对象分配到不同的区域之后,垃圾收集器才可以针对不同的区域进行垃圾回收,因此才会出现"Minor GC", “Major GC”, "Full GC"这样的回收类型的划分。也能够根据不同的区域安排与里面存储对象存亡特征相匹配的垃圾收集算法:标记-复制标记-清除标记-整理

有了分代回收,可以解决大部分常见的回收问题,但还有一个问题就是跨代引用,即新生代的对象被老年代所引用。这种情况下,回收新生代对象时,就不得不再去扫描整个老年代区域。为解决这个问题,需要对分代设计思想添加第三条法则:

  1. 跨代引用假说(Intergenerational Reference Hypothesis):跨代引用相对于同代引用来说,只存在于少数情况。
    根据上面这条假说,为了减少跨代引用所扫描老年代带来的性能问题,解决办法是在新生代上建立一个全局的数据结构,称为记忆集 Remembered Set,这个结构把老年代划分成若干小块,标识出老年代的哪一块内存有跨代引用,以后当发生 Minor GC时,只有包含了跨代引用的小块内存里的对象,才会被加入到 GC Roots集合中进行扫描。

GC 名词解释:

  部分收集 (Partial GC): 目标不是完整的收集整个 Java 堆。

  新生代收集 (Minor GC / Young GC): 收集新生代的垃圾收集器。

  老年代收集 (Major GC / Old GC): 收集老年代的垃圾收集器。目前只有 CMS 收集器会有单独收集老年代的行为。而且不同资料对`Major GC` 的定义同,需要根据上下文来判断。

  混合收集 (Mixed GC): 收集新生代和部分老年代的垃圾收集。目前只有 G1有这种行为。

  整堆收集 (Full GC): 收集整个 Java 堆和方法区的垃圾收集。
垃圾收集方式

串行: 一个垃圾收集线程
并行 Parallel: 描述的是多条垃圾收集线程之间的关系,同一时间有多条垃圾收集线程在同时工作,通常默认此时用户线程处于等待状态
并发: 描述的是垃圾收集线程用户线程之间的关系,说明垃圾收集线程用户线程一起工作(但不一定是并行的,可能会交替执行),用户程序在继续执行,而垃圾收集程序在另一个CPU上。由于 用户线程占用了一部分资源,此时程序的处理吞吐量将受到一定的影响。

"精典"垃圾收集算法
标记-清除:

经历2个步骤:标记垃圾对象清除垃圾对象
标记-清除 算法有2个缺点:

  1. 标记、清理2步效率都不稳定,当内存中有大量对象需要回收时,标记、清理需要进行大量工作,效率就会降低。
  2. 标记、清理完成后,内存区域会产生很多碎片,遇到大对象可能没有足够的内存分配。

image.png

标记-复制算法:【【【 适用于 新生代 】】】

标记-复制算法又称为复制算法。
算法的思想是:将可用内存一分为2,每次只使用其中一块内存,如果内存不够时,进行垃圾回收,将存活的对象拷贝到另一块内存中。
算法的优点是:实现简单,运行高效。
算法的缺点是:
1 内存的利用利不高。
2 复制算法在 对象存活效长时,就需要进行较多的复制,效率会降低。不适合 老年代的垃圾回收。
3 垃圾回收时,如果还是有大量对象存活,没有足够的内存为新对象分配内存,则会依赖其他内存,如老年代,分配担保。如果另一块 Survivor 没有足够的空间来 存储 存活下来的对象,那么这些对象,将直接进入老年代。就像银行借钱,需要担保人一样。

image.png

直接把可用内存1分为2过于浪费空间,之后研究出另一种更高效的内存配置比例:把新生代分成一块较大的 Eden 空间和两块较小的Survivor 空间,每次分配内存只使用Eden 空间和其中一块Survivor 空间,当发生垃圾回收时,就把Eden 空间Survivor 空间中存活的对象统一复制到另一块Survivor 空间中。

HotSpot 虚拟机的 Serial, ParNew 等新生代收集器均采用了这种设计部局。空间默认大小是 8:1,即: Eden大小是8,2个Survivor大小是1。

这种设计虽然提高了内存的利用率,减少了空间的浪费,有一个问题是无法保证在垃圾回收之后,存活的对象大小 小于 另一块Survivor 空间的大小。而当 存活对象大小 大于 另一块 Survivor 空间的大小时,需要借助其他内存区域(实际上大多是老年代)进行分配担保。

标记-整理:【【【 适用于 老年代 】】】

标记-整理: 与标记-清理算法的本质差别在于:标记-清理是一种 非移动式的回收算法,而标记-整理移动式的回收算法。
标记-整理算法的标记过程与标记-清理的标记过程一样,不同之处在于标记-清理算法在标记完成后,把回收的对象清除掉。而标记-整理算法是将存活对象向可用空间的一头移动,然后清除掉边界以外的空间。
标记-整理 算法经历3个步骤:标记垃圾对象,清除垃圾对象,空间合并。

image.png

HotSpot 虚拟机里面关注吞吐量Parallel Scavenge收集器是基于标记-整理的。关注延迟CMS 收集器是基于标记-清理的。为了解决 内存分配访问效率上平衡的问题,CMS收集器采用了一种混合的方式,即:一般情况下让虚拟机采用标记-清理的方式,暂时容忍内存碎片,当内存碎片已经影响到大对象分配时,再采用标记-整理法算收集一次。

HotSpot 实现细节

暂时跳过
分代回收算法:【【【 适用于 老年代 】】】

  • 大多数的商业虚拟机都采用 分代 垃圾回收。分代垃圾回收 的思想是,针对对象存活时间长短,采取不同的策略。
精典的垃圾收集器

这里讨论的是 JDK7 Update 4之后,JDK11正式发布之前 HotSpot 虚拟机中所包含的全部可用垃圾收集器。使用精典二字是为了与之后的高性能低延迟的收集器区分开来。

image.png

上图两个收集器存在连线,则说明两个收集器可以搭配使用。
在 JDK8中 Serial + CMS,ParNew + Serial Old 这两种组合被声名名废弃。到了 JDK9 已经完全取消了这些组件的支持。

新生代:
  Serial,ParNew,Parallel Scavenge

老年代:
  Serial Old,CMS,Parallel Old

通用:
  G1垃圾收集并没有使用传统的GC收集器代码,而是 另外独立实现。
Serial

Serial是【新生代】【单线程】【复制算法】【Stop-The-World】收集器,也是Client模式下,新生代默认的收集器。
优点:在单CPU,单核心的情况下Serial是最高效的收集器,没有多线程的并发开销,他是所有收集器里额外内存占用最小的。

image.png

Serial Old:

Serial Old 是【老年代】【单线程】【标记-整理】【Stop-The-World】收集器,也是客户端 Client模式下,老年代默认的收集器。CMS 收集器发生失败后 Serial Old 作为后备预案,在并发收集发生 Concurrent Mode Failure时使用。

image.png

ParNew

ParNew是【新生代】【多线程】【复制算法】【Stop-The-World】收集器。他是Serial的多线程版本,多线程以外,其他行为包括:控制参数,收集算法,Stop-The-World,对象分配规则,回收策略都与Serial一样。在 JDK7之前,HotSpot 虚拟机在Server模式下新生代首选的收集器,而且只有 ParNew 能与 CMS一起合作。
ParNew 是激活 CMS (-XX:+UseConcMarkSweepGC)之后的默认新生代收集器,也可以使用-XX:+/-UseParNewGC 来强制使用或禁用它。

但是随着 G1 收集器的出现 ,CMS + PawNew 组合逐渐退出历史。G1是一款面向全堆的收集器,不需要与其他新生代收集器配合工作。从 JDK9开始,CMS + ParNew就不再是官方推荐的服务端模式下的收集器解决方案了。JDK9之后,官网甚至取消了 ParNew + Serial OldSerial + CMS 两组收集器的组合,并取消了-XX:UserParNewGC参数,这意味着 ParNew 与 CMS 只能互相搭配使用。

在单线程环境中,由于存在 线程相互开销,PawNew 处理器的不会比 Serial 收集器有更好的收集效果。但是在多核心情况下,PawNew 对系统资源的回收还是非常高效的。多核CPU 核心模式下,开启的线程数默认与CPU核数相同,可以通过 -XX:ParallelGCThreads 指定启动的线程数。

Paralle Scavenge

Paralle Scavenge 【新生代】【多线程】【复制算法】【关注吞吐量】收集器。
CMS收集器关注的是低延迟,尽可能缩短垃圾收集时用户线程的停顿时间,而 Paralle Scavenge 收集器的目标是: 达到一个可控的吞吐量。吞吐量 = 用户运行代码时间 / (用户运行代码时间 + GC收集时间)
Paralle Scavenge 收集器提供了直接设置吞吐量大小参数:-XX:GCTimeRatio,及 控制最大垃圾收集停顿时间参数:-XX:MaxGCPauseMills
-XX:MaxGCPauseMills: 参数允许设置一个是大于0的毫秒数,收集器将尽力保证回收花费的时候不超过该值。用户不要异想天开的认为,把该值设置的小一点,垃圾回收的速度就会更快,垃圾收集停顿的时间的缩短是以牺牲吞吐量新生代空间为代价的。也就是说,收集的停顿时间变短,垃圾收回的次数就会增加,吞吐量就会降低。
-XX:GCTimeRatio:参数的值应该设置一个0到100的整数,即垃圾收集时间占总时间的比率,相当于吞吐量的倒数。参数设置为19,则垃圾收集时间占比为5%,即 1/(1+19),默认值是99,即允许最大1%的垃圾收集时间。
由于 Paralle Scavenge 与吞吐量密切相关,Paralle Scavenge 又称为吞吐量优先收集器。它还有一个-XX:UseAdaptiveSizePolicy参数,开启了该参数后,就不需要指出新生代大小(-Xmn),Eden与 Survivior区域的比例(-XX:SurvivorRatio),晋升老年代对象大小(-XX:PretenureSizeThreshold)等参数了,JVM 将收集运行信息监控运行状态,自动调节每个参数的值。这种方式称为垃圾收集的自适应调节策略
自适应调节也是Parallel Scavenge收集器与ParNew收集器的一个重要特性。

Paralle Old

Paralle Old 是Paralle Scavenge 的老年代版本,是【老年代】【多线程】【标记-整理】【吞吐量】收集器。该收集器直到 JDK6才开始提供使用。
在注重吞吐量或处理资源较为稀缺的场合,都可以优先考虑 Parallel Scavenge + Parallel Old 这种组合。

image.png

CMS

CMS是【老年代】【多线程】【标记-清除】【Stop-The-World】关注响应时间的收集器。
CMS经过以下几个步骤:

  1. 初始标记(CMS initial mark):Stop The World停止全部,只是标记一下GC Roots 能直接关联到的对象,速度很快。
  2. 并发标记(CMS concurrent mark):与用户线程一起,是从GC Roots直接关联对象开始遍历整个对象图的过程,过程较长,但是与用户线程一起并发执行。
  3. 重新标记(CMS remark):Stop The World停止全部,重新标记是 修正 并发标记阶段用户程序继续运行,而导致标记产生变动的一部分对象的标记,这个阶段停顿时间通常比初始标记阶段稍长,但远远低于并发标记阶段。
  4. 并发清除:清理掉标记阶段判断已经死亡的对象,由于不需要移动存活对象,此阶段与用户线程一起。

image.png

CMS 收集器的缺点:
  • CMS 收集器对CPU资源很敏感。CMS 不会导致用户线程停顿,但因为占用了一部分线程而导致应用程序变慢,降低吞吐量。CMS 默认启动的线程数是 (处理器核心数+3) / 4,在处理器核心数在4个或以上时,并发垃圾回收占用的系统资源不足25%。但是处理器核心数不足4个时,CMS 对用户程序的影响就可能变得很大。

  • CMS 收集器无法处理浮动垃圾,有可能出现Concurrent Mode Failure失败进而导致另一次完全的Stop The World的 Full GC 产生。由于并发标记并发清除是与用户线程一起执行的,所以就会产生额外的垃圾对象,这些垃圾对象都是发生在标记, 清理过程之后,所以只能等到一次垃圾回收,才能回收该对象。而这些垃圾就称为浮动垃圾。由于 CMS 是垃圾回收线程与用户线程并发执行的,所以老年代必须要预留足够的空间为用户线程提供使用,在 JDK5中,当 CMS 收集器在老年代使用了68%的时候会启动,这个值有点保守。在 JDK6中,CMS 在老年代启动的阀值已经默认提到92%。但这又可能会出现另一个问题:如果预留的内存无法满足程序分配新对象的需要,就会出现一次并发失败 (Concurrent Mode Failure), 这时候虚拟机将不得不启动后备预案:冻结用户线程的执行,临时启动 Serial Old 收集器重新对老年代的垃圾收集,但这样的停顿时间很长了。

  • CMS 是一个基于 ‘标记-清除’的收集器,所以会产生大量的内存碎片,当分配大对象时那就可能出现内存空间足够,但是不得不进行 Full GC 的情况。但它提供了一个 -XX:UseCMSCompactAtFullCollection 开关参数,默认是开启的,但在 JDK9开始废弃,用于在 CMS 收集器在顶不住,要进行FullGC时,开启碎片整理过程。这样碎片问题就解决了,但停顿的时间会变长,因此虚拟机设计者提供了另外一个参数-XX:CMSFullGCsBeforeCompaction(此参数 JDK9开始废弃),该参数要求 CMS 收集器在执行若干次不整理空间的 Full GC 之后,下一次进入 Full GC 前会先进行碎片整理,默认值为0,表示每次进入 Full GC 时都需要进行碎片整理。

Garbage First
Shenandoah

image.png

–> GC日志解读
4.231: [GC 4.231: [DefNew: 4928K->512K(4928K), 0.0044047 secs] 6835K->3468K(15872K), 0.0045291 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

4.445: [Full GC (System) 4.445: [Tenured: 2956K->3043K(10944K), 0.1869806 secs] 4034K->3043K(15872K), [Perm : 3400K->3400K(12288K)], 0.1870847 secs] [Times: user=0.05 sys=0.00, real=0.19 secs]  

GC 和 Full GC: 是说垃圾收集的类型,有full表示发生了 Stop-The-World。

35333.562: GC发生的时间。
DefNew: GC发生的区域,垃圾收集器的名子

4928KK					->512K 					(4928K)
GC前该内存区域的使用量		GC后该内存区域的使用量		内存区域的总量.

0.0044047: GC发生了多长时间

6835K				->3468K					(15872K)
GC前Java堆的使用量		GC后Java堆的使用量			Java堆的总量


[Times: user=0.05 sys=0.00, real=0.19 secs] 
	:用户消耗CPU时间  系统消耗CPU时间 real: 墙钟时间,包括各种非CPU使用所包含的时间,比如IO等待等。

–> 内存分配与回收策略
->. 对象优先有Eden上分配
如果在Eden上没有足够空间,那进行一次 Minor GC. 在GC完成之后,发现要回收的对象无法完全进入 Survivor,那分配担保机制将起作用,将对象都转入 老年代,并把新对象分配在 Eden中。

->. 大对象直接进入老年代
	所谓的大对象是指需要连接存储空间的对象,比如Java数组。虚拟机提供了这样的参数:-XX:PretenureSizeThreshold,大于参数指定值的对象,将直接进入老年代。PretenureSizeThreshold只对 Serial 和 ParNew有效。

->. 长期存活的对象将进入老年代
	每个对象都有一个对象年龄,对象年龄是指 每次 垃圾回收一次,对象年龄加1,等到到一定值,默认15,时就直接进入老年代。Java虚拟机通过 MaxTenuringThreshold来设置该在数。

->. 动态对象年龄判定
	虚拟机并不是硬性要求,所有对象必须达到 阀值才能进入老年代。而是在Survivor空间中相同年龄的对象的总大小 大于 Survivor的一半,那么这批对象将进入老年代。

–> JVM内存参数
-Xms small JVM
-Xmx max JVM最大使用内容
-Xmn new JVM新生代内存
-Xss 128k 设置每个线程的堆栈大小
-XX:MaxPermSize=16m 设置持久代大小为16m。
-XX:SurvivorRatio: 8:1 代表 新生代中 Eden 和 两个 Survivor的比值.
-XX:PrintGCDetail 打印GC日志
-XX:PretenureSizeThreshold
-XX:MaxTenuringThreshold
-XX:MaxNewSize=size 新生代最大大小。
-XX:MaxTenuringThreshold=threshold 在新生代中对象存活次数(经过Minor GC的次数)后仍然存活,就会晋升到旧生代。
-XX:MetaspaceSize=size 设置类元空间大小。
-XX:MinHeapFreeRatio=percent 堆最小空间百分比。
-XX:NewRatio=ratio 设置新生代和老年代的比例。
-XX:NewSize=size 设置年轻代的大小
-XX:ParallelGCThreads=threads 并行收集线程数量。
-XX:+PrintGC 打印GC信息。
-XX:+PrintGCDetails 打印gc详细信息


Java 监控工具

–> jps
Java进和一监控工具,可以显示下在运行的Java进程
-l 列出运行主类的全路径
-m 列出运行时的提交的参数
-v 列出运行主类启动时JVM参数

–> jstat
Java统计监视工具,它可以显示:虚拟机进程中的 类装载,内存,垃圾收集等运行时数据。
-gc 监视Java 堆状况。
-class 监视类信息,
-gcutil 与gc基本相同,但是主要关注已经使用空间占比
-gccause 与 gcutil 功能一样,但是会额外导出 上一次GC产生的原因

–> jinfo
实时的查看和调整虚拟机的各项参数.

–> Jstack
用于生成 虚机机 当前时刻的线程快照。
线程快照 就是当前虚拟机每一条线程正在执行的方法 栈的集合,主要用来定位 线程 出现长时间停顿的原因。

–> Jmap
用来生成过堆的转储快照。

–> JHat
用来分析 Jmap 生成的堆转储快照。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值