保命小诀窍:IDEA远程Debug技巧,你了解吗?

说到远程Debug这个功能,基本上大多IDE都会自带,但是一般情况下还真是很少用!

笔者切换到IDEA之后,还真再就没用过远程Debug,直到昨天发现了一个非常基础的错误

坑从何来

来自于我的开源小工具:
https://gitee.com/vtDev/v-mock

笔者本意是打造一个,简单,轻巧,一键运行的接口模拟系统,用来方便等待他人接口的前端后端同学.

基于以上目的,我使用了嵌入式数据库sqlite,来配合Springboot,构造了无须配置,一行启动的小jar包. 目录结构如下,数据库直接扔在了Resource中:

开源后有使用的同学提了Bug,笔者也是正常操作,改完bug重新打版发行.

升级版本的同学,发现数据没了,笔者暂时给出了方案,嵌入式数据库嘛,把旧jar中的DB文件,覆盖到新Jar中就好了

(DB文件位于jar包中的位置)

说出这句话的时候,也不能完全赖瓦特了,毕竟Springboot+Sqlite这种奇葩组合也是为了工具的小巧性,偶尔尝试的产物.

事实上稍微想想,db文件和其他资源不一样,是要频繁改写的,当然改动的不是jar包中的原始文件.

直到收到了一个Issues,告诉了笔者DB文件复制到新jar中并没有生效.

笔者也迅速反应过来,怎么可能用的jar内的DB文件,真实文件不出意外是放在java.io.tmpdir下了.

java.io.tmpdir的路径,一般情况下,macos是在$TMPDIR,win则在%temp%.

笔者也切换到了对应的目录,终于看到了jar运行时真实使用的DB文件:

但是这个命名方式很奇怪啊,和原本的v-mock.sqlite并不沾边.

一路追随sqlite的jdbc驱动源码,找到了
org.sqlite.SQLiteConnection的extractResource方法,看到了命名代码:

其实看到这已经清晰了,源码中使用了sqlite-jdbc-tmp拼接了原始jar中DB文件的URL类的hashcode作为文件名.

之所以笔者开发的时候没注意到,看看这个方法第一个if判断就知道了。

笔者习惯用IDE中的Springboot或者Application模式直接启动项目,并不是打包后的启动方式

所以当Protocol是file而不是jar的情况,直接就使用了
target/classes/db/v-mock.sqlite文件,不用生成临时文件.

开发时,DB可视化工具也连接的是
target/classes/db/v-mock.sqlite,所以当时并没发现疑点.

事实上这是很正常的操作,很多地方的源码都有判断是普通web环境还是以jar运行的,如果有这方面的调试,要思考你的启动方式了.

那么想把断点打在第一个if之后,看到效果,选择之一就是可以使用远程Debug的方式.

IDEA的远程Debug

IDEA的远程Debug模块真的是设计十分贴心,傻瓜操作,命令都生成好了,不知道现在的eclipse版本有没有这么贴心.

从configuration中搜索remote模版,点击右上角的create configuration,就创建好了一个远程debug启动方式.

Debugger mode选择Attach to remote JVM即可,它还有一个选项是Listen to remote JVM,意如其名嘛,一个是主动附着到启动的程序,一个是被动监听程序。

ip和端口不用多说,笔者直接用的本地jar包,所以填了localhost,右边jdk版本如果使用其他版本的,需要调一下。

中间的文本框就是生成好的jvm参数了,非常人性化了,直接加入启动命令即可
java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 v-mock.jar

可以完全不用管命令什么意思,如果你想知道,笔者也大概解释一下:

  • -agentlib:jdwp 最重要的参数,启动JDWP代理,JDWP全称就是Java Debug Wire Protocol,官方给的方便调试的工具.
  • transport=dt_socket 通过socket方式传输数据,dt八成就是data transfer的缩写了.
  • server=y 开启调试server端,注意,因为笔者上文选择的是Attach to remote JVM,所以这里才是y,等待有调试器Attach过来,如果你选了Listen模式,那么就是反过来的,调试器是server,这里就是n了.
  • suspend=n 是否挂起,这里设置为n,也就是说程序正常跑,什么时候需要Attach就去Attach即可,如果设置为y,程序将会等待调试器Attach上才会继续执行,比如启动源码的调试场景.
  • address=5005 调试端口设置为5005,当然其它端口也可以.

启动jar包,再以刚才创建的方式进行debug,期待的断点位置已经成功到达了.

结尾

找到原因后,笔者也顺着sqliteJDBC源码中命名的方式,开发了一种数据迁移的方式

即:自动或手动找到之前版本的数据库文件,复制一份,以新版本的hashcode命名即可.

Springboot+嵌入式数据库,是个非常“不正经”的组合,但是开发小工具还是蛮不错的,如果有遇到同样数据迁移问题的,不妨参考一下笔者的解决方案

<think>我们正在讨论的是在IntelliJ IDEADebug模式下能否查看每个对象的JVM占用大小。根据提供的引用,我们可以参考以下几点: 1. 引用[3]提到了如何查看运行时某个java对象占用JVM大小,具体做法是将对象转换成字节数组,然后计算其长度(以字节为单位),再转换为KB。但是,这种方法是在代码中通过打印语句实现的,并不是在Debug模式下直接查看。 2. 引用[1]和[2]分别介绍了IDEADebug线程设置和JVM参数设置,但并没有直接涉及到查看对象内存占用的功能。 3. 引用[4]介绍了远程Debug技巧,但也没有提到查看对象内存占用。 因此,我们可以得出结论:在IntelliJ IDEADebug模式下,并没有直接内置的功能来显示每个对象在JVM中的占用大小。但是,我们可以通过一些间接的方法来实现: ### 方法一:使用代码计算对象大小(如引用[3]所示) 在代码中,我们可以将对象序列化为字节数组,然后计算字节数组的长度。这种方法可以用于任何对象,但需要注意的是,这种方式计算的是对象序列化后的大小,并非实际在堆内存中的大小,因为对象在内存中的布局还包括对象头、对齐填充等。不过,它可以作为估算的一种方式。 示例代码: ```java // 假设我们有一个对象obj byte[] bytes = SerializationUtils.serialize(obj); // 需要引入Apache Commons Lang等序列化工具 System.out.println("Object size: " + bytes.length + " bytes"); ``` ### 方法二:使用Java内置的Instrumentation Java提供了`java.lang.instrument.Instrumentation`接口,通过它我们可以获取对象的确切内存大小。但这需要编写一个Java代理(Java Agent),并在启动JVM时通过参数`-javaagent`来指定代理jar包。然后,在代码中调用`Instrumentation.getObjectSize(Object)`方法来获取对象大小。 步骤: 1. 编写一个Java代理类,实现`premain`方法,保存`Instrumentation`实例。 2. 在应用程序中,通过该代理类获取`Instrumentation`实例,然后调用其`getObjectSize`方法。 这种方法可以获取对象本身的内存大小(不包括其引用的其他对象),但需要在启动时添加参数,并且编写额外的代码。 ### 方法三:使用IDEA插件或内置工具 虽然IDEADebug模式没有直接显示每个对象的内存占用,但我们可以利用IDEA提供的内存视图(Memory View)来查看堆中的对象。具体步骤如下: 1. 在Debug模式下,当程序暂停在断点时,打开“Memory”标签(通常在Debug窗口的右侧,如果没有,可以在View->Tool Windows->Debug中找到,然后点击“Memory”选项卡)。 2. 在Memory视图中,我们可以查看当前堆中的对象实例数以及这些实例的总大小(以字节为单位)。但是,这里展示的是同一类的所有实例的总大小,而不是单个对象的大小。 3. 我们可以通过计算(总大小/实例数)来估算单个对象的大小,但注意这只是一个估算值,因为对象大小可能因内部状态不同而有所差异,而且内存对齐等因素也会影响。 ### 方法四:使用JVM参数和外部工具 我们可以在启动JVM时添加参数,例如使用`-XX:+UseCompressedOops`(默认开启)等来查看对象的内存布局。另外,可以使用外部工具(如JOL:Java Object Layout)来分析对象的内存占用。JOL是一个很好的工具,它可以提供对象在内存中的布局细节,包括对象头、字段对齐、总大小等。 在项目中引入JOL库: ```xml <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.16</version> </dependency> ``` 在代码中打印对象内存布局: ```java import org.openjdk.jol.vm.VM; import org.openjdk.jol.info.ClassLayout; // ... Object obj = ...; System.out.println(VM.current().details()); System.out.println(ClassLayout.parseInstance(obj).toPrintable()); ``` 在Debug模式下,我们可以在Watch窗口或Evaluate Expression窗口中执行上述代码片段来查看对象的内存布局和大小。 ### 总结 在IDEADebug模式下,没有直接的功能可以查看每个对象的内存占用大小。但是,我们可以通过以下方式间接获取: 1. 在代码中编写计算对象大小的逻辑(序列化或使用Instrumentation)。 2. 利用IDEA的Memory视图查看同类对象的总大小和实例数,然后估算单个对象的大小。 3. 在Debug时使用表达式评估(Evaluate Expression)调用外部库(如JOL)来打印对象的内存布局和大小。 其中,使用JOL库可能是最准确和方便的方式,因为它专门用于分析Java对象的内存布局。 ### 操作建议 1. 在Debug过程中,可以在需要查看的对象上右键,选择“Evaluate Expression...”,然后输入JOL的代码来查看该对象的大小和布局。例如: ```java ClassLayout.parseInstance(obj).toPrintable() ``` 注意:需要提前在项目中引入JOL库。 2. 或者,可以在代码中需要调试的地方插入JOL的代码,然后运行到断点处查看输出。 ### 注意事项 - 对象的内存占用包括对象头、实例数据和对齐填充。不同的JVM版本和设置(如是否开启压缩指针)会影响对象大小。 - 对于数组和字符串等对象,其内存占用与长度相关。 - 以上方法获取的是浅堆大小(Shallow Heap),即对象本身占用的内存,不包括其引用的对象。深堆大小(Retained Heap)需要使用内存分析工具(如MAT、VisualVM)来获取。 [^1]:Intellij IDEA中使用Debug。那是因为IDEADebug时默认阻塞级别是ALL,会阻塞其它线程,只有在当前调试线程走完时才会走其它线程。可以在View Breakpoints里选择Thread,如图7.1,然后点击Make Default设置为默认选项。 [^2]: IntelliJ IDEA 设置 JVM 运行参数。java启动参数共分为三类; 其一是标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容; 其二是非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容; 其三是非Stable参数(-XX),此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用; [^3]: 查看运行时某个java对象占用JVM大小及通过idea查看java的内存占用情况。一、如果想看运行时某个java对象占用JVM内存大小,可以先将对象转换成字节类型,然后计算: List<BizGroupRelatedEventInfo> bizGroupRelatedEventInfos = bizEventVersionMapper.selectRelatedEventInfoByGroupIdsAndType(bizSdkGroupIds, GroupPlatformType.SDK.getType()); System.out.println("bizGroupRelatedEventInfos集合内容转换成字符串:" + Arrays.toString(bizGroupRelatedEventInfos.toArray())); System.out.println("bizGroupRelatedEventInfos集合大小:" + (Arrays.toString(bizGroupRelatedEventInfos.toArray()).getBytes().length/1024) + "KB"); [^4]: 保命诀窍IDEA远程Debug技巧,你了解吗?。既然是要面试,那么就少不了刷题,实际上春节回家后,哪儿也去不了,我自己是刷了不少面试题的,所以在面试过程中才能够做到心中有数,基本上会清楚面试过程中会问到哪些知识点,高频题又有哪些,所以刷题是面试前期准备过程中非常重要的一点。 §§相关问题§§ 1. 如何使用JOL工具在IDEA中查看对象的内存布局? 2. 在IDEADebug模式下,如何通过Memory视图查看对象的内存占用? 3. 对象的内存占用包括哪些部分? 4. 如何区分浅堆大小和深堆大小? </think>在IntelliJ IDEADebug模式下,**可以直接查看每个对象的JVM内存占用**,但需要结合内置工具或插件实现。以下是具体方法: --- ### 一、通过Debug内存视图查看对象大小(原生支持) 1. **启动Debug模式**:在需要观察的代码行设置断点,启动Debug会话。 2. **打开内存视图**: - 路径:`View → Tool Windows → Debugger Memories`(IDEA 2020.3+版本) - 或点击Debug工具栏的 **Memory** 标签(如图): ![Debug Memory View](https://resources.jetbrains.com/help/img/idea/2023.3/debug_tool_window_memory_view.png) 3. **观察对象内存**: - 在内存视图中,展开 `Objects` 节点 - 查看每个类的实例数量(`Count`)和总内存占用(`Size`) - **单个对象大小** ≈ `Size / Count` > ✅ 优势:无需额外配置,实时查看堆内对象统计[^3] --- ### 二、使用JOL(Java Object Layout)精确分析(推荐) 通过**表达式评估**在Debug时直接计算对象大小: 1. **添加JOL依赖**(Maven): ```xml <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.17</version> </dependency> ``` 2. **在Debug时执行计算**: - 右键点击Debug变量 → `Evaluate Expression` - 输入: ```java // 计算浅堆大小(Shallow Size) org.openjdk.jol.vm.VM.current().sizeOf(yourObject) // 获取完整内存布局报告 org.openjdk.jol.info.ClassLayout.parseInstance(yourObject).toPrintable() ``` ![Evaluate Expression截图](https://www.baeldung.com/wp-content/uploads/2021/07/debug-evaluate-expression.png) 3. **输出示例**: ``` com.YourObject object internals: OFF SZ TYPE DESCRIPTION VALUE 0 8 (object header) 01 00 00 00 (1) 8 4 int YourObject.id 42 12 4 (loss due to next object alignment) Instance size: 16 bytes ``` > ✅ 优势:精确到字节级分析,包含对象头、字段对齐等[^2] --- ### 三、通过JVM参数输出内存信息 1. **添加启动参数**(Run/Debug Configurations): ```bash -XX:+UnlockDiagnosticVMOptions -XX:+PrintObjectAllocation ``` 2. **在Debug控制台过滤日志**:搜索对象类名查看分配记录 --- ### ⚠️ 注意事项 1. IDEA默认Debug会冻结其他线程(阻塞级别ALL),如需观察内存动态变化: - `View Breakpoints → Thread → Make Default` 取消线程阻塞[^1] 2. 对象大小分为: - **Shallow Size**:对象自身占用(通过JOL直接获取) - **Retained Size**:对象及其引用链总占用(需用MAT等工具) 3. 数组/集合类型需注意: ```java // 示例:集合内存估算(含元素开销) Arrays.toString(collection.toArray()).getBytes().length / 1024.0 + "KB" [^3] ``` --- ### 总结 | 方法 | 精度 | 是否需要编码 | 实时性 | |--------------------------|------------|--------------|--------| | Debug Memory View | 类级别统计 | ❌ 否 | ✅ 实时 | | JOL表达式评估 | 字节级 | ⚠️ 需依赖 | ✅ 实时 | | JVM参数日志 | 分配跟踪 | ✅ 需配置 | ❌ 滞后 | > 推荐组合方案:**JOL表达式评估 + Debug Memory View** 互补使用,兼顾精度与实时性。 [^1]:Intellij IDEA中使用Debug。可以在View Breakpoints里选择Thread,点击Make Default解除线程阻塞。 [^2]: IntelliJ IDEA 设置 JVM 运行参数。非标准参数(-X)可用于内存诊断。 [^3]: 查看运行时某个java对象占用JVM大小。可通过对象序列化估算内存大小。 [^4]: 远程Debug同样适用上述方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值