一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏),应届生面试java开发工程师的题库及答案

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

mw.visitMethodInsn(INVOKEVIRTUAL, “java/io/PrintStream”, “println”, “(Ljava/lang/String;)V”);

mw.visitInsn(RETURN);

mw.visitMaxs(2, 2);

mw.visitEnd(); //字节码生成完成

return cw.toByteArray(); // 获取生成的class文件对应的二进制流

}

}

package com.q.mat;

import java.util.*;

import org.objectweb.asm.*;

public class ThreadAndListHolder extends ClassLoader implements Opcodes {

private static Thread innerThread1;

private static Thread innerThread2;

private static final SameContentWrapperContainerProxy sameContentWrapperContainerProxy = new SameContentWrapperContainerProxy();

static {

// 启用两个线程作为 GC Roots

innerThread1 = new Thread(new Runnable() {

public void run() {

SameContentWrapperContainerProxy proxy = sameContentWrapperContainerProxy;

try {

Thread.sleep(60 * 60 * 1000);

} catch (Exception e) {

System.exit(1);

}

}

});

innerThread1.setName(“ThreadAndListHolder-thread-1”);

innerThread1.start();

innerThread2 = new Thread(new Runnable() {

public void run() {

SameContentWrapperContainerProxy proxy = proxy = sameContentWrapperContainerProxy;

try {

Thread.sleep(60 * 60 * 1000);

} catch (Exception e) {

System.exit(1);

}

}

});

innerThread2.setName(“ThreadAndListHolder-thread-2”);

innerThread2.start();

}

}

class IntArrayListWrapper {

private ArrayList list;

private String name;

public IntArrayListWrapper(ArrayList list, String name) {

this.list = list;

this.name = name;

}

}

class SameContentWrapperContainer {

// 2个Wrapper内部指向同一个 ArrayList,方便学习 Dominator tree

IntArrayListWrapper intArrayListWrapper1;

IntArrayListWrapper intArrayListWrapper2;

public void init() {

// 线程直接支配 arrayList,两个 IntArrayListWrapper 均不支配 arrayList,只能线程运行完回收

ArrayList arrayList = generateSeqIntList(10 * 1000 * 1000, 0);

intArrayListWrapper1 = new IntArrayListWrapper(arrayList, “IntArrayListWrapper-1”);

intArrayListWrapper2 = new IntArrayListWrapper(arrayList, “IntArrayListWrapper-2”);

}

private static ArrayList generateSeqIntList(int size, int startValue) {

ArrayList list = new ArrayList(size);

for (int i = startValue; i < startValue + size; i++) {

list.add(i);

}

return list;

}

}

class SameContentWrapperContainerProxy {

SameContentWrapperContainer sameContentWrapperContainer;

public SameContentWrapperContainerProxy() {

SameContentWrapperContainer container = new SameContentWrapperContainer();

container.init();

sameContentWrapperContainer = container;

}

}

启动参数:-Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/gjd/Desktop/dump/heapdump.hprof

-XX:-UseCompressedClassPointers -XX:-UseCompressedOops

引用关系图

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

分析过程

  1. 首先进入 Dominator tree,可以看出是 SameContentWrapperContainerProxy 对象与 main 线程两者持有99%内存不能释放导致 OOM。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

先来看方向一,在 Heap Dump Overview中可以快速定位到 Number of class loaders 数达50万以上,这种基本属于异常情况,如下图所示。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

使用 Class Loader Explorer 分析工具,此时会展现类加载详情,可以看到有524061个 class loader。我们的案例中仅有ClassLoaderOOMOps 这样的自定义类加载器,所以很快可以定位到问题。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

如果类加载器较多,不能确定是哪个引发问题,则可以将所有的 class loader对象按类做聚类,如下图所示。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

Histogram 会根据 class 聚合,并展现对象数量及其 Shallow Heap 及 Retained Heap(如Retained Heap项目为空,可以点击下图中计算机的图标并计算 Retained Heap),可以看到 ClassLoaderOOMOps 有524044个对象,其 Retain Heap 占据了370M以上(上述代码是100M左右)。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

使用 incoming references,可以找到创建的代码位置。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

再来看方向二,同样在占据319M内存的 Obejct 数组采用 incoming references 查看引用路径,也很容易定位到具体代码位置。并且从下图中我们看出,Dominator tree 的起点并不一定是 GC根,且通过 Dominator tree 可能无法获取到最开始的创建路径,但 incoming references 是可以的。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

3. 对象间依赖详解及实战

3.1 References

==============

注:笔者使用频率 Top2

功能:在对象引用图中查看某个特定对象的所有引用关系(提供对象对其他对象或基本类型的引用关系,以及被外部其他对象的引用关系)。通过任一对象的直接引用及间接引用详情(主要是属性值及内存占用),提供完善的依赖链路详情。

使用入口:目标域右键 → List objects → with outgoing references/with incoming references.

使用场景

  • outgoing reference:查看对象所引用的对象,并支持链式传递操作。如查看一个大对象持有哪些内容,当一个复杂对象的 Retained Heap 较大时,通过 outgoing reference 可以查看由哪个属性引发的。下图中 A 支配 F,且 F 占据大量内存,但优化时 F 的直接支配对象 A 无法修改。可通过 outgoing reference 看关系链上 D、B、E、C,并结合业务逻辑优化中间环节,这依托 dominator tree 是做不到的。

  • incoming reference:查看对象被哪些对象引用,并支持链式传递操作。如查看一个大对象都被哪些对象引用,下图中 K 占内存大,所以 J 的 Retained Heap 较大,目标是从 GC Roots 摘除 J 引用,但在 Dominator tree 上 J 是树根,无法获取其被引用路径,可通过 incoming reference 查看关系链上的 H、X、Y ,并结合业务逻辑将 J 从 GC Root 链摘除。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

3.2 Thread overview

功能:展现转储 dump 文件是线程执行栈、线程栈引用的对象等详细状态,也提供各线程的 Retained Heap 等关联内存信息。

使用入口:MAT 主页 → Thread overview

使用场景

  • 查看不同线程持有的内存占比,定位高内存消耗线程(开发技巧:不要直接使用 Thread 或 Executor 默认线程名避免全部混合在一起,使用线程尽量自命名方便识别,如下图中 ThreadAndListHolder-thread 是自定义线程名,可以很容易定位到具体代码)

  • 查看线程的执行栈及变量,结合业务代码了解线程阻塞在什么地方,以及无法继续运行释放内存,如下图中 ThreadAndListHolder-thread 阻塞在 sleep 方法。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

3.3 Path To GC Roots

功能:提供任一对象到 GC Root 的路径详情。

使用入口:目标域右键 → Path To GC Roots

使用场景:有时你确信已经处理了大的对象集合但依然无法回收,该功能能快速定位异常对象不能被 GC 回收的原因,直击异常对象到 GC Root 的引用路径。比 incoming reference 的优势是屏蔽掉很多不需关注的引用关系,比 Dominator tree 的优势是可以得到更全面的信息。

小技巧:在排查内存泄漏时,建议选择 exclude all phantom/weak/soft etc.references 排除虚引用/弱引用/软引用等的引用链,因为被虚引用/弱引用/软引用的对象可以直接被 GC 给回收,聚焦在对象是否还存在 Strong 引用链即可。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

3.4 class loader 分析

功能

  • 查看堆中所有 class loader 的使用情况(入口:MAT 主页菜单蓝色桶图标 → Java Basics → Class Loader Explorer)。

  • 查看堆中被不同class loader 重复加载的类(入口:MAT 主页菜单蓝色桶图标 → Java Basics → Duplicated Classes)。

使用场景

  • 当从 Heap dump overview 了解到系统中 class loader 过多,导致占用内存异常时进入更细致的分析定位根因时使用。

  • 解决 NoClassDefFoundError 问题或检测 jar 包是否被重复加载

具体使用方法在 2.6 及 3.5 两节的案例中有介绍。

3.5 综合案例二

使用工具项:class loader(重复类检测)、inspector、正则检索。

异常现象 :运行时报 NoClassDefFoundError,在 classpath 中有两个不同版本的同名类。

分析过程

  1. 进入 MAT 已加载的重复类检测功能,方式如下图。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

可以看到所有重复的类,以及相关的类加载器,如下图。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

  • 根据类名,在框中输入类名可以过滤无效信息。

  • 选中目标类,通过Inspector视图,可以看到被加载的类具体是在哪个jar包里。(本例中重复的类是被 URLClassloader 加载的,右键点击 “_context” 属性,最后点击 “Go Into”,在弹出的窗口中的属性 “_war” 值是被加载类的具体包位置)

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

4. 对象状态详解及实战

4.1 inspector

=============

功能:MAT 通过 inspector 面板展现对象的详情信息,如静态属性值及实例属性值、内存地址、类继承关系、package、class loader、GC Roots 等详情数据。

使用场景

  • 当内存使用量与业务逻辑有较强关联的场景,通过 inspector 可以通过查看对象具体属性值。比如:社交场景中某个用户对象的好友列表异常,其 List 长度达到几亿,通过 inspector 面板获取到异常用户 ID,进而从业务视角继续排查属于哪个用户,本里可能有系统账号,与所有用户是好友。

  • 集合等类型的使用会较多,如查看 ArrayList 的 size 属性也就了解其大小。

举例:下图中左边的 Inspector 窗口展现了地址 0x125754cf8 的 ArrayList 实例详情,包括 modCount 等并不会在 outgoing references 展现的基本属性。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

4.2 集合状态

========

功能:帮助更直观的了解系统的内存使用情况,查找浪费的内存空间。

使用入口:MAT 主页 → Java Collections → 填充率/Hash冲突等功能。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

使用场景

  • 通过对 ArrayList 或数组等集合类对象按填充率聚类,定位稀疏或空集合类对象造成的内存浪费。

  • 通过 HashMap 冲突率判定 hash 策略是否合理。

具体使用方法在 4.3 节案例详细介绍。

4.3 综合案例三

使用工具项:Dominator tree、Histogram、集合 ratio。

异常现象 :程序 OOM,且 Dominator tree 无大对象,通过 Histogram 了解到多个 ArrayList 占据大量内存,期望通过减少 ArrayList 优化程序。

程序代码

package com.q.mat;

import java.util.ArrayList;

import java.util.List;

public class ListRatioDemo {

public static void main(String[] args) {

for(int i=0;i<10000;i++){

Thread thread = new Thread(new Runnable() {

public void run() {

HolderContainer holderContainer1 = new HolderContainer();

try {

Thread.sleep(1000 * 1000 * 60);

} catch (Exception e) {

System.exit(1);

}

}

});

thread.setName(“inner-thread-” + i);

thread.start();

}

}

}

class HolderContainer {

ListHolder listHolder1 = new ListHolder().init();

ListHolder listHolder2 = new ListHolder().init();

}

class ListHolder {

static final int LIST_SIZE = 100 * 1000;

List list1 = new ArrayList(LIST_SIZE); // 5%填充

List list2 = new ArrayList(LIST_SIZE); // 5%填充

List list3 = new ArrayList(LIST_SIZE); // 15%填充

List list4 = new ArrayList(LIST_SIZE); // 30%填充

public ListHolder init() {

for (int i = 0; i < LIST_SIZE; i++) {

if (i < 0.05 * LIST_SIZE) {

list1.add(“” + i);

list2.add(“” + i);

}

if (i < 0.15 * LIST_SIZE) {

list3.add(“” + i);

}

if (i < 0.3 * LIST_SIZE) {

list4.add(“” + i);

}

}

return this;

}

}

分析过程

  1. 使用 Dominator tree 查看并无高占比起点。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

使用 Histogram 定位到 ListHolder 及 ArrayList 占比过高,经过业务分析很多 List 填充率很低,不会浪费内存。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

查看 ArrayList 的填充率,MAT 首页 → Java Collections → Collection Fill Ratio。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

查看类型填写 java.util.ArrayList。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

从结果可以看出绝大部分 ArrayList 初始申请长度过大。

一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)

5. 按条件检索详解及实战

5.1 OQL

功能:提供一种类似于SQL的对象(类)级别统一结构化查询语言,根据条件对堆中对象进行筛选。

语法

SELECT * FROM [ INSTANCEOF ] <class_name> [ WHERE ]

  • Select 子句可以使用“*”,查看结果对象的引用实例(相当于 outgoing references);可以指定具体的内容,如 Select OBJECTS v.elementData from xx 是返回的结果是完整的对象,而不是简单的对象描述信息);可以使用 Distinct 关键词去重。

  • From 指定查询范围,一般指定类名、正则表达式、对象地址。

  • Where 用来指定筛选条件。

  • 全部语法详见:OQL 语法

  • 未支持的核心功能:group by value,如果有需求可以先导出结果到 csv 中,再使用 awk 等脚本工具分析即可。

例子:查找 size=0 且未使用过的 ArrayList:select * from java.util.ArrayList where size=0 and modCount=0。

使用场景

  • 一般比较复杂的问题会使用 OQL,而且这类问题往往与业务逻辑有较大关系。比如大量的小对象整体占用内存高,但预期小对象应该不会过多(比如达到百万个),一个一个看又不现实,可以采用 OQL 查询导出数据排查。

例如:微服务的分布式链路追踪系统,采集各服务所有接口名,共计200个服务却采集到了200万个接口名(一个服务不会有1万个接口),这时直接在 List 中一个个查看很难定位,可以直接用 OQL 导出,定位哪个服务接口名收集异常(如把 URL 中 ID 也统计到接口中了)

5.2 检索及筛选

功能:本文第二章内存分布,第三章对象间依赖的众多功能,均支持按字符串检索、按正则检索等操作。

使用场景:在使用 Histogram、Thread overview 等功能时,可以进一步添加字符串匹配、正则匹配条件过滤缩小排查范围。

5.3 按地址寻址

功能:根据对象的虚拟内存十六进制地址查找对象。

使用场景:仅知道地址并希望快速查看对象做后续分析时使用,其余可以直接使用 outgoing reference 了解对象信息。

5.4 综合案例四

使用工具项:OQL、Histogram、incoming references

异常现象及目的 :程序占用内存高,存在默认初始化较长的 ArrayList,需分析 ArrayList 被使用的占比,通过数据支撑是否采用懒加载模式,并分析具体哪块代码创建了空 ArrayList。

程序代码

难道这样就够了吗?不,远远不够!

提前多熟悉阿里往年的面试题肯定是对面试有很大的帮助的,但是作为技术性职业,手里有实打实的技术才是你面对面试官最有用的利器,这是从内在散发出来的自信。

备战阿里时我花的最多的时间就是在学习技术上,占了我所有学习计划中的百分之70,这是一些我学习期间觉得还是很不错的一些学习笔记

我为什么要写这篇文章呢,其实我觉得学习是不能停下脚步的,在网络上和大家一起分享,一起讨论,不单单可以遇到更多一样的人,还可以扩大自己的眼界,学习到更多的技术,我还会在csdn、博客、掘金等网站上分享技术,这也是一种学习的方法。

今天就分享到这里了,谢谢大家的关注,以后会分享更多的干货给大家!

阿里一面就落马,恶补完这份“阿里面试宝典”后,上岸蚂蚁金服

阿里一面就落马,恶补完这份“阿里面试宝典”后,上岸蚂蚁金服

image.png

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

rrayList,需分析 ArrayList 被使用的占比,通过数据支撑是否采用懒加载模式,并分析具体哪块代码创建了空 ArrayList。

程序代码

难道这样就够了吗?不,远远不够!

提前多熟悉阿里往年的面试题肯定是对面试有很大的帮助的,但是作为技术性职业,手里有实打实的技术才是你面对面试官最有用的利器,这是从内在散发出来的自信。

备战阿里时我花的最多的时间就是在学习技术上,占了我所有学习计划中的百分之70,这是一些我学习期间觉得还是很不错的一些学习笔记

我为什么要写这篇文章呢,其实我觉得学习是不能停下脚步的,在网络上和大家一起分享,一起讨论,不单单可以遇到更多一样的人,还可以扩大自己的眼界,学习到更多的技术,我还会在csdn、博客、掘金等网站上分享技术,这也是一种学习的方法。

今天就分享到这里了,谢谢大家的关注,以后会分享更多的干货给大家!

[外链图片转存中…(img-dOsBlcbz-1713672447867)]

[外链图片转存中…(img-VTRkUCIf-1713672447868)]

[外链图片转存中…(img-96vzrHW9-1713672447868)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-ggnEITje-1713672447869)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
面试Java开发工程师时,JVMJava虚拟机)是一个重要的面试话题。以下是一些常见的JVM面试题及其答案: 1. 什么是JVMJVMJava虚拟机的缩写,它是Java程序运行的环境。它负责将Java字节码翻译成机器码,并提供内存管理、垃圾回收等功能。 2. JVM的组成部分有哪些? JVM由三个主要组成部分组成: - 类加载器(ClassLoader):负责将类文件加载到内存中。 - 运行时数据区(Runtime Data Area):包括方法区、堆、栈等内存区域。 - 执行引擎(Execution Engine):负责执行字节码指令。 3. 什么是垃圾回收(Garbage Collection)? 垃圾回收是JVM自动管理内存的过程。它会自动识别不再使用的对象,并释放它们所占用的内存空间,以便其他对象可以使用。 4. 什么是Java堆(Java Heap)? Java堆是JVM中最大的一块内存区域,用于存储对象实例。所有通过new关键字创建的对象都会被分配到Java堆中。 5. 什么是方法区(Method Area)? 方法区是JVM中用于存储类信息、常量、静态变量等的内存区域。它是所有线程共享的。 6. 什么是栈(Stack)? 栈是JVM中用于存储方法调用和局部变量的内存区域。每个线程都有自己的栈,用于保存方法调用的上下文信息。 7. 什么是字节码(Bytecode)? 字节码是Java源代码编译后生成的中间代码,它由一系列指令组成,可以在JVM上执行。 8. 什么是即时编译(Just-In-Time Compilation)? 即时编译是JVM在运行时将热点代码(被频繁执行的代码)编译成本地机器码的过程,以提高程序的执行效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值