写点什么

深入了解 JAVA 篇之内存分析

作者:邱学喆
  • 2022 年 8 月 14 日
    广东
  • 本文字数:12805 字

    阅读完需:约 42 分钟

深入了解JAVA篇之内存分析

一. 概述

在《JVM 之内存管理》文章介绍了 jvm 的内存组成结构以及垃圾回收器算法。当运行时发生了内存溢出,或者说频繁的做 GC 操作,间接到其吞吐量,性能低效;那么我们该如何进行分析,找出问题,从而解决性能问题;也是这篇所要介绍的内容;

当频繁做 GC 时,意味有如下几种可能性:

  • 内存规划不充足;

  • 对象依赖链不合理,导致对象一直在老年代(Full GC)

  • 对象管理不合理(Minor GC)

当内存泄露时,意味有如下几种可能性:

  • 内存规划不充足;

  • 对象存活时间过长,或者一直驻留在 jvm 内存中;

接下来,就针对如上可能性去排查定位,具体是哪种可能性导致的;

二. 内存划分

在分析之前,是需要知道 jvm 内存组成结构,如何设置各个内存区域块的内存大小;

在《JVM 之内存管理》文章中介绍了组成结构,我们主要是分析虚拟机栈,方法区和堆内存;


我们启动 java 应用程序时,如果不指定任何参数,jvm 会帮我们选择哪种【垃圾回收器】、栈大小、方法区内存大小,堆内存大小;

下面是使用 JDK1.8 环节下,当启动时,我们通过 jps、jmap 命令来查看其配置信息:

//jps => jmap -heap pid......Heap Configuration:   MinHeapFreeRatio         = 0    MaxHeapFreeRatio         = 100   MaxHeapSize              = 4087349248 (3898.0MB)//最大堆大小   NewSize                  = 84934656 (81.0MB)//新生代初始内存的大小   MaxNewSize               = 1362100224 (1299.0MB)//表示新生代可被分配的内存最大上限   OldSize                  = 170917888 (163.0MB)//老年代的默认大小,当实际老年代不只这么小;   NewRatio                 = 2 //老年代对比新生代的空间大小, 比如2代表老年代空间是新生代的两倍大小   SurvivorRatio            = 8 //8表示Survivor:Eden=1:8   MetaspaceSize            = 21807104 (20.796875MB) //分配给类元数据空间的初始大小   CompressedClassSpaceSize = 1073741824 (1024.0MB) //类指针压缩空间大小, 默认为1G   MaxMetaspaceSize         = 17592186044415 MB//分配给类元数据空间的最大值,    														//超过此值就会触发Full GC,这个值可以忽略;   G1HeapRegionSize         = 0 (0.0MB).........
复制代码

通过 jinfo 命令也可以看到 jvm 的参数以及垃圾回收器:

//jinfo pid.......VM Flags:Non-default VM flags: 	//内存相关的参数	-XX:CICompilerCount=12 -XX:InitialHeapSize=255852544   -XX:MaxHeapSize=4087349248 -XX:MaxNewSize=1362100224 -XX:MinHeapDeltaBytes=524288   -XX:NewSize=84934656 -XX:OldSize=170917888   //垃圾回收器相关的参数  -XX:+UseCompressedClassPointers -XX:+UseCompressedOops   -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation   -XX:+UseParallelGC//Parallel Scavenge + Serial Old 的收集器组合进行内存回收 .......
复制代码


2.1 栈

In the HotSpot implementation, Java methods 【share stack frames】 with C/C++ native code,namely user native code and the virtual machine itself. Java methods generate code that checks whether stack space is available a fixed distance towards the end of the stack so that the native code can be called without exceeding the stack space. This distance towards the end of the stack is called "Shadow Pages". The size of the shadow pages is between 【3 and 20 pages】, depending on the platform. This distance is tunable, so that applications with native code needing more than the default distance can increase the shadow page size. The option to increase shadow pages is 【-XX:StackShadowPages=n】, where n is greater than the default stack shadow pages for the platform.
复制代码

官网中的介绍,也就是意味着与操作系统保持一致,可以说无限制;如果真的出现特殊场景,那就通过【-XX:StackShadowPages=n】来拓展栈内存大小;在我本地电脑,通过命令可以查看其设置大小

//jinfo -flag StackShadowPages 11864-XX:StackShadowPages=6
复制代码

在 32 位操作系统,一页大小为 4K。可以看出栈大小为 24K。

If you increase the value of StackShadowPages, you might also need to increase the default thread stack size using the 【-Xss】 parameter.Increasing the default thread stack size might 【decrease the number of threads】 that can be created, so be careful in choosing a value for the thread stack size. The thread stack size varies by platform from 256 KB to 1024 KB.
复制代码

如果提高了栈大小,那我们可能需要去提高线程栈大小,通过【-Xss】(也可以换一种写法【-XX:ThreadStackSize=?】)参数来设置,从而限制线程数量。

从上面的描述里看,线程栈大小与线程数量有直接关系,具体是什么关系;目前还未得知。

有关栈的介绍就到这里,这个知识点跟内存分析没有直接关系,可以说顺带提一句。

2.2 元数据区

Class MetadataJava classes have an internal representation within Java Hotspot VM and are referred to as class metadata. In previous releases of Java Hotspot VM, the class metadata was allocated in the so called permanent generation. In JDK 8, the permanent generation was removed and the class metadata is allocated 【in native memory】. The amount of native memory that can be used for class metadata is by default unlimited. Use the option 【MaxMetaspaceSize】 to put an upper limit on the amount of native memory used for class metadata.
Java Hotspot VM explicitly manages the space used for metadata. Space is requested from the OS and then divided into chunks. A class loader allocates space for metadata from its chunks (a chunk is bound to a specific class loader). When classes are unloaded for a class loader, its chunks are recycled for reuse or returned to the OS. Metadata uses space allocated by mmap, not by malloc.
复制代码

官网上的介绍,意味着从 JDK1.8 后,不再叫【方法区】,而是叫做【元数据区】。同时是直接使用 OS 提供的内存,我们 可以通过一下命令参数来限制内存大小;

-XX:MetaspaceSize=?//初始化元数据区内存大小-XX:MaxMetaspaceSize=?//最大元数据区内存大小
复制代码

既然是直接使用操作系统内存,也不算 JVM 内存分析的范畴,暂且掠过。

2.3 堆

堆又分为新生代和老年代;新生代又再次分为 Eden 区和两个 Survivor 区;复制上面的 jmap 得到的堆内存情况;

MaxHeapSize              = 4087349248 (3898.0MB)//最大堆大小NewSize                  = 84934656 (81.0MB)//新生代初始内存的大小MaxNewSize               = 1362100224 (1299.0MB)//表示新生代可被分配的内存最大上限OldSize                  = 170917888 (163.0MB)//老年代的默认大小,当实际老年代不只这么小;NewRatio                 = 2 //老年代对比新生代的空间大小, 比如2代表老年代空间是新生代的两倍大小SurvivorRatio            = 8 //8表示Survivor:Eden=1:8
复制代码

我们得到新生代内存大小 1299MB,其中 Eden 区大小为 1039.2MB,每个 Survivor 区大小 129.9MB;老年代;老年代是 2598MB;

相关的参数设置如下:

最大堆大小:-Xmx?或者-XX:MaxHeapSize=3898m新生代初始化堆大小:-Xms81m 或者-XX:NewSize=81m新生代比例:-XX:NewRatio=8Survivor比例:-XX:SurvivorRatio=8
复制代码

堆内存大小的设置以及如何查看其大小,是内存分析的前提;

三. HPROF 格式

当内存泄露或者我们手工将 JVM 的内存 dump 下来,其格式内容如下:

上面的 dump 格式内容,我这里省略很多不同标签下的内容;感兴趣的,可以去看其源代码【heapDumper.cpp】(其实我也没有完成的阅读完其代码,只是看了关键的几个部分代码而已)。

尝试在官网中查找有关 hprof 文件格式的介绍,一直没有找到,索性就把源代码中有关 hprof 文件格式介绍拷贝过来:

/* * HPROF binary format - description copied from: *   src/share/demo/jvmti/hprof/hprof_io.c * * *  header    "JAVA PROFILE 1.0.2" (0-terminated) 版本号 * *  u4        size of identifiers. Identifiers are used to represent *            UTF8 strings, objects, stack traces, etc. They usually *            have the same size as host pointers. For example, on *            Solaris and Win32, the size is 4.  指针占用内容大小,常规4或者时8 * u4         high word 8字节的时间戳,目前没有发现其具体用途,先略过 * u4         low word    number of milliseconds since 0:00 GMT, 1/1/70  * [record]*  a sequence of records. * * * Record format: * * u1         a TAG denoting the type of the record 标签类型 * u4         number of *microseconds* since the time stamp in the  *            header. (wraps around in a little more than an hour) *						目前没有发现其具体用途,先略过 * u4         number of bytes *remaining* in the record. Note that *            this number excludes the tag and the length field itself. *						body所占用内存大小 * [u1]*      BODY of the record (a sequence of bytes) 内容值 * * * The following TAGs are supported: * * TAG           BODY       notes *---------------------------------------------------------- * HPROF_UTF8               a UTF8-encoded name  常量池的内容 * *               id         name ID 符号表指针 *               [u1]*      UTF8 characters (no trailing zero) * * HPROF_LOAD_CLASS         a newly loaded class  新加载的类,可以不用看 * *                u4        class serial number (> 0) *                id        class object ID *                u4        stack trace serial number *                id        class name ID * * HPROF_UNLOAD_CLASS       an unloading class  被卸载的类,可以不用看 * *                u4        class serial_number * * HPROF_FRAME              a Java stack frame  java栈帧内容 * *                id        stack frame ID   栈帧编号 *                id        method name ID 方法名指针 *                id        method signature ID 修饰符指针, *                id        source file name ID 类指针 *                u4        class serial number 类编号 *                i4        line number. >0: normal  代码对应的函数 *                                       -1: unknown *                                       -2: compiled method *                                       -3: native method * * HPROF_TRACE              a Java stack trace 栈trace * *               u4         stack trace serial number 栈编号 *               u4         thread serial number 线程编码 *               u4         number of frames 栈帧数量 *               [id]*      stack frame IDs 栈帧编号集合 * * * HPROF_ALLOC_SITES        a set of heap allocation sites, obtained after GC  * 目前没有理解其含义,可以先略过 *               u2         flags 0x0001: incremental vs. complete *                                0x0002: sorted by allocation vs. live *                                0x0004: whether to force a GC *               u4         cutoff ratio *               u4         total live bytes *               u4         total live instances *               u8         total bytes allocated *               u8         total instances allocated *               u4         number of sites that follow *               [u1        is_array: 0:  normal object *                                    2:  object array *                                    4:  boolean array *                                    5:  char array *                                    6:  float array *                                    7:  double array *                                    8:  byte array *                                    9:  short array *                                    10: int array *                                    11: long array *                u4        class serial number (may be zero during startup) *                u4        stack trace serial number *                u4        number of bytes alive *                u4        number of instances alive *                u4        number of bytes allocated *                u4]*      number of instance allocated * * HPROF_START_THREAD       a newly started thread. 新线程,可以先略过 * *               u4         thread serial number (> 0) *               id         thread object ID *               u4         stack trace serial number *               id         thread name ID *               id         thread group name ID *               id         thread group parent name ID * * HPROF_END_THREAD         a terminating thread. 中止的线程,可以先掠过 * *               u4         thread serial number * * HPROF_HEAP_SUMMARY       heap summary 堆汇总内容,在jdk中并没有填充该内容,可以掠过 * *               u4         total live bytes *               u4         total live instances *               u8         total bytes allocated *               u8         total instances allocated * * HPROF_HEAP_DUMP          denote a heap dump 整个堆内容所存放地方; * *               [heap dump sub-records]*  子项 * *                          There are four kinds of heap dump sub-records: * *               u1         sub-record type 子项类型 * *               HPROF_GC_ROOT_UNKNOWN         unknown root 未知GC root对象 * *                          id         object ID 对象指针 * *               HPROF_GC_ROOT_THREAD_OBJ      thread object 线程GC root对象 * *                          id         thread object ID 线程对象指针 (may be 0 for a *                                     thread newly attached through JNI) *                          u4         thread sequence number 线程编号 *                          u4         stack trace sequence number 线程trace编号 * *               HPROF_GC_ROOT_JNI_GLOBAL      JNI global ref root  * *                          id         object ID *                          id         JNI global ref ID * *               HPROF_GC_ROOT_JNI_LOCAL       JNI local ref * *                          id         object ID *                          u4         thread serial number *                          u4         frame # in stack trace (-1 for empty) * *               HPROF_GC_ROOT_JAVA_FRAME      Java stack frame 栈帧对象 * *                          id         object ID 对象ID *                          u4         thread serial number 线程编号 *                          u4         frame # in stack trace (-1 for empty) 栈帧编号 * *               HPROF_GC_ROOT_NATIVE_STACK    Native stack 本地栈 * *                          id         object ID 对象指针 *                          u4         thread serial number 线程编号 * *               HPROF_GC_ROOT_STICKY_CLASS    System class 系统类 * *                          id         object ID 系统对象指针 * *               HPROF_GC_ROOT_THREAD_BLOCK    Reference from thread block 阻塞线程引用 * *                          id         object ID 对象指针 *                          u4         thread serial number 线程编号 * *               HPROF_GC_ROOT_MONITOR_USED    Busy monitor 监控锁 * *                          id         object ID 对象指针 * *               HPROF_GC_CLASS_DUMP           dump of a class object 类 * *                          id         class object ID 类对象指针 *                          u4         stack trace serial number 无意义,略过 *                          id         super class object ID 父类指针 *                          id         class loader object ID 加载类指针 *                          id         signers object ID 略过 *                          id         protection domain object ID 略过 *                          id         reserved 略过 *                          id         reserved 略过 * *                          u4         instance size (in bytes) 类所占用内存大小 * *                          u2         size of constant pool 常量池,jdk默认填充为0,可以略过 *                          [u2,       constant pool index, *                           ty,       type *                                     2:  object *                                     4:  boolean *                                     5:  char *                                     6:  float *                                     7:  double *                                     8:  byte *                                     9:  short *                                     10: int *                                     11: long *                           vl]*      and value * *                          u2         number of static fields 静态类属性数量 *                          [id,       static field name, 静态类属性值 *                           ty,       type, *                           vl]*      and value * *                          u2         number of inst. fields (not inc. super) 实例对象属性数量 *                          [id,       instance field name, 实例对象属性描述 *                           ty]*      type * *               HPROF_GC_INSTANCE_DUMP        dump of a normal object 实例对象 * *                          id         object ID 对象指针 *                          u4         stack trace serial number 无意义,略过 *                          id         class object ID 类对象指针 *                          u4         number of bytes that follow 实例对象属性所占用内存大小 *                          [vl]*      instance field values (class, followed *                                     by super, super's super ...) 实例对象属性值 * *               HPROF_GC_OBJ_ARRAY_DUMP       dump of an object array 引用类型的实例对象数组 * *                          id         array object ID 对象指针 *                          u4         stack trace serial number 无意义,略过 *                          u4         number of elements 数组元素数量 *                          id         array class ID 类性指针 *                          [id]*      elements 数组元素值 * *               HPROF_GC_PRIM_ARRAY_DUMP      dump of a primitive array 基础类型的对象数组 * *                          id         array object ID 对象指针 *                          u4         stack trace serial number 无意义,略过 *                          u4         number of elements 数组元素数量 *                          u1         element type 数组元素类型 *                                     4:  boolean array *                                     5:  char array *                                     6:  float array *                                     7:  double array *                                     8:  byte array *                                     9:  short array *                                     10: int array *                                     11: long array *                          [u1]*      elements 数组元素值 * * HPROF_CPU_SAMPLES        a set of sample traces of running threads 不在这篇讨论范畴,略过 * *                u4        total number of samples *                u4        # of traces *               [u4        # of samples *                u4]*      stack trace serial number * * HPROF_CONTROL_SETTINGS   the settings of on/off switches不再这篇讨论范畴,略过 * *                u4        0x00000001: alloc traces on/off *                          0x00000002: cpu sampling on/off *                u2        stack trace depth * * * When the header is "JAVA PROFILE 1.0.2" a heap dump can optionally * be generated as a sequence of heap dump segments. This sequence is * terminated by an end record. The additional tags allowed by format * "JAVA PROFILE 1.0.2" are: * * HPROF_HEAP_DUMP_SEGMENT  denote a heap dump segment * *               [heap dump sub-records]* *               The same sub-record types allowed by HPROF_HEAP_DUMP * * HPROF_HEAP_DUMP_END      denotes the end of a heap dump * */
复制代码

3.1 汇总

从上面 hprof 文件格式,我们从中得到关键的信息:

  • 类加载器都加载哪些类

  • 类、对象占用内存大小

  • GC root 对象

  • 线程对象以及线程栈帧对象是占用堆内存

四. 内存分析

MAT 工具,这里使用的是 eclipse MAT 工具;而我使用的独立版 1.13.0;

这里不全面介绍该工具的所有知识点,只是把关键的部分进行介绍;

写这篇文章之前,尝试阅读其源代码,仓库如下;

https://git.eclipse.org/r/mat/org.eclipse.mat.git
复制代码

但发现要了解其细节原理,前提是必须把 hotspot 源代码给读过一篇;所花的精力与时间是非常庞大了,索性就靠模拟以及测试验证我的猜想;待后续时间充足后,在研究其源码;

下面的有关的例子,是基于一下代码进行的:

package com.michael.mat;
import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicLong;
/** * 这里演示top consumer中的object具体指的啥意思? */public class HeapTopConsumerMain { /** * 验证oldSize是否会自增 * vm参数: * -Xmx500m * -XX:+HeapDumpOnOutOfMemoryError * -XX:HeapDumpPath=. * -XX:MaxMetaspaceSize=20m * -XX:NewRatio=4 * -XX:PretenureSizeThreshold=5m * -XX:MaxTenuringThreshold=5 */ public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> { WorkObject.out(100,6); }); thread.start(); WorkObject.out(4,6); }
public static class WorkObject { public static void out(int size,int loop) { new HeapOutMemory(size).play(loop); } }
public static class HeapOutMemory { private int size; List<byte[]> data = new ArrayList<>(); public HeapOutMemory(int size) { this.size = size; }
public void play(int loop) {
AtomicLong atomicLong = new AtomicLong(0); for (int i = 0; i < loop; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } long count = atomicLong.getAndIncrement(); if (count < 5) { byte[] bytes = new byte[1024 * 1024 * size]; data.add(bytes);//一次创建size[MB]内存 } System.out.println("当前线程" + Thread.currentThread().getName() + ",技术器:" + count++ + ",数组项:" + data.size()); } } }
}
复制代码

4.1 概念介绍

在进行分析之前,先介绍 eclipse 的 MAT 工具中的几个概念;

1. Dominator Tree

如果按照单词的中文翻译【控制树】,可能不利于理解;在这里,我这里将其成为【依赖树】;

箭头指针代表依赖的意思;例如 A->C,指的是 A 实例对象依赖 C 对象,如果从代码层面上理解,对象 C 是对象 A 的属性,还有很多代码方式是这种表示形式。

2. Shallow vs. Retained Heap

  • Shallow 代表这个对象或者类自身所占用内存大小;

  • Retained Heap 代表这个对象或者类自身所占用内存+自身依赖的对象或者类所占用内存大小;

eclipse 官网中有这么一个例子,可以拿到简单说明一下:


3. 可达性

我们可以理解为【依赖树】另外一种说法;

4.2 OQL 语法

OQL 语法跟 SQL 有点类似,只是缺少了聚合等操作,只能查看单个类或者实例对象的相关信息,对于分析而言,没有多大的参考价值;就没有进行简述;但可以尝试去阅读其源代码,往里面增加有关聚合操作特性;这里就不再介绍,相关的内容,帮助文档都写得很清楚;下面是具体的语法表示形式;


4.3 对象

1. Top Consumer

整个堆内存基本上是对象的天下,所以第一个要分析出来的,哪个对象占用堆内存最大,

通过【Open Query Browser】->【Leak Identification】->【Top Consumer】就能看到饼图占比,来看其对象占用的比例;

对应的源代码:TopConsumers2Query,大体的逻辑如下:

不填写相关的筛选条件前提下:1. 设置阈值:已经使用的堆内存大小的1%;2. 遍历找到大于阈值的对象;这里的对象,主要是指一旦这个对象要销毁,其所占用的内存也会被释放;
复制代码

我这里输入过滤条件,发现报告有所相似,如下图:

通过上面这两张图,很多细节都被掩盖住,具体是哪个对象占用内存过大导致的;我们单机该对象选中【List objects】->【with outgoing references】,如下图:

得到该对象依赖哪些对象,如下图:

发现该线程使用到了 HeapOutMemory 对象,该对象使用 ArrayList 对象,ArrayList 对象中有三个数组元素,每个数组元素都占用 100MB;这下就明白内存泄露的原委了;

2. Leak Suspects

上面只是罗列出来的,还有一个更快捷的方式,通过内存泄露报告;我们通过【Overview】单击【Leak Suspects】如下图:

又或者【Open Query Browser】->【Leak Identification】->【Find Leaks】,如下图:

它会弹出选择框,我们修改阈值比例为 1,如下图:

他们最终得到的报告如下:



相关源代码在 LeakHunterQuery

4.4 GC root

我们通过【Open Query Browser】->【Java Basics】->【GC Boots】,如下图:

在我这个例子中,有一下对象是作为 root 得;

我们可以看到各个 root 对象分别占用得内存大小;

上图得例子中,我们可以看到占用大量内存得对象分别是 Thread-0 以及 main 这两个线程对象;

4.5 线程

一般来说,线程基本是 root 对象;如果某个线程得逻辑中使用了大量堆内存;当前对象是最先展示出来,表明其占用内存得相关信息;但还需要进一步挖掘。那么这里就涉及到栈帧得知识点;

我们通过鼠标右键选中目标线程对象,选择【Thread Overview and Stacks】,如下图:

就可以看到该线程得整个栈帧的信息,如下图:

从上面的图示来看,在代码上的第 56 处导致了内存泄露,同时也得知红色圈出来的栈帧所消耗的内存较大;再继续深入,可以得知这个栈帧使用到一个对象 HeapOutMemory 超大;我们就可以找到其具体的代码进行分析;

4.6 类加载器

我们可以通过类加载器,来统计都加载了哪些类,以及创建了多少个对象;我们通过【Open Query Browser】->【Java Basics】->【Class Loader Explorer】,如下图:

得到有关类加载器相关信息;

从上面的图,是不是看不出对内存泄露或者对代码优化没啥作用;

我这里举个场景,大概就能明白了;

在一些复杂应用系统中,会频繁的创建大量的对象以及销毁的现象;那么我们直接通过这个报告,就立马可以看到哪个对象的数量最大;从而去尝试去做代码优化,例如可以采用对象管理器来处理等;

虽然在【Top Consumer】进一步分析,也是能看出来,但不够这个报告简单明了;

五. 汇总

回到最开始的提出的问题:

频繁的做 GC,以及内存泄露的情景时,我们该怎么分析其具体的原因;

当频繁做 MinorGC 时:

1. 通过jmap等命令行工具来查看新生代的内存使用情况;2. 通过【Unreachable Objects Histogram】来找到将要释放的对象信息,	并对应的找到相关代码,查看其使用情况;	试图分析,是不是由于这些对象使用不合理,或者有没有优化空间;(这一块很难复现其场景,就没有罗列出来);3. 如果没有,那么就尝试扩大新生代内存,以及Eden区与Survivor区的比例;
复制代码

当频繁做 MinorGC 时:

1. 通过jmap等命令行工具来查看新生代的内存使用情况;2. 通过【Leak Suspects】和【Top Consumer】找出可能内存泄露的对象使用情况,	并参考其代码尝试发现是否有使用不合理情况;3. 如果发现使用合理,接着通过【GC root】和【线程】,来分析各个栈帧的内存使用情况,看是否有问题;4. 如果还没问题,那么【类加载器】报告,查看对象数量是否有问题;5. 依然没有问题,那就扩容;在整个环节中,检查是否有问题,同时还要思考是否有优化空间等;
复制代码

当内存泄露时:

1. 通过【GC root】和【线程】来找出泄露的发生的代码,尝试发现其问题所有;2. 无问题,回看【GC root】中各个root对象所占用的内存大小;3. 结合【Leak Suspects】和【Top Consumer】报告来查看检验;
复制代码

一般来说,当出现问题时,具体的问题是较为隐晦的,通过 MAT 工具分析,很难一步到位就定位处问题,需要反复的 JVM 调整参数,代码逻辑等方式去排查;也就是说 MAT,分析出来的数据,仅供参考;


另外,MAT 上面还有很多工具提供,由于本人尚未遇到过生产环境的频繁做 GC,内存泄露等情况,所以上面所介绍的内容,仅仅是在我本地调试验证,以及自己的理解;而且所用的例子,也较为简单;

所以,仅供参考。


在内存分析时,我们还要结合垃圾回收器、以及操作系统层面上的相结合,才能较为准确的定位出问题;

由于这两块的源代码,并没有过多阅读,待后续有时间会单独介绍;

发布于: 刚刚阅读数: 3
用户头像

邱学喆

关注

计算机原理的深度解读,源码分析。 2018.08.26 加入

在IT领域keep Learning。要知其然,也要知其所以然。原理的爱好,源码的阅读。输出我对原理以及源码解读的理解。个人的仓库:https://gitee.com/Michael_Chan

评论

发布
暂无评论
深入了解JAVA篇之内存分析_JVM_邱学喆_InfoQ写作社区