一次疑似 JVM native 内存泄漏的排查实录

Android Native内存泄漏检测方案详解 本文介绍了四种Android Native层检测内存泄漏的方法:AddressSanitizer、LeakSanitizer、Valgrind和手动检测。这些方法有助于开发者预防和解决内存泄漏。 阅读详情

最近开发同学反馈,某定时任务服务疑似有内存泄漏,整个进程的内存占用比 Xmx 内存大不少,而且看起来是缓慢上升的,做了下面这次分析,包括下面的内容:

  • 分析 JVM native 内存的一些常见思路
  • 内存增长了,怎么甄别是不是内存泄漏
  • 一个完全不熟悉的项目如何找到可能导致 native 内存分配的代码
  • 经典的 Linux 64M 内存问题
  • 到底是内存碎片还是内存泄漏

现象

这个定时任务的应用设置 Xmx 为 925M,但是 native 内存缓存持续增长,但是增长到一定阶段也会保持稳定,不再继续增长。

是内存泄漏吗?

不管是不是内存泄漏,首先要搞清楚的是这段增长的内存是什么,土方法就是用 pmap -x 持续观察内存地址空间的变化。

经过几个小时的 pmap 后台运行,很快发现堆内存几乎无变化,增长的区域都在 64M 内存空间,这就是经典的 glibc 内存分配 64M 问题。

关于 Linux 64M 内存问题,我之前写过几篇相关的文章,大家感兴趣可以去看。

从这里基本可以确定是 native 带来的问题,接下来就是 dump 出来看里面到底存了什么。这里有几个方法

  • 使用 gdb
  • 写一个脚本读取 /proc/<pid>/mem
  • 我自己用 Go 写的一个小工具(可能过段时间释放出来)

脚本内容如下:

cat /proc/$1/maps | grep -Fv ".so" | grep " 0 " | awk '{print $1}' | grep $2 | ( IFS="-"
while read a b; do
dd if=/proc/$1/mem bs=$( getconf PAGESIZE ) iflag=skip_bytes,count_bytes \
skip=$(( 0x$a )) count=$(( 0x$b - 0x$a )) of="$1_mem_$a.bin"
done )

执行这个脚本,传入进程号和起始地址就可以把对应内存 dump 到文件中。接下来可以通过 strings 初步查看文件里面有没有认识的字符串。通过 strings 发现很多 jar 包文件里的内容,部分内容如下:

这个内容是项目依赖 jar 包 HikariCP-2.5.1.jar 的 MANIFEST.MF 文件的内容

.
├── MANIFEST.MF
└── maven
    └── com.zaxxer
        └── HikariCP
            ├── pom.properties
            └── pom.xml

看来就是程序就是读了 HikariCP-2.5.1.jar 的内容,通过 16 进制分析可以进一步确认。众所周知 jar 包就是一个 zip,如果读取了 zip,那理论内存中会有 zip 的魔数,问一下 ChatGPT zip 的魔数是多少。

用 010 Editor 拿着 50 4B 03 04 去内存里搜,可以看到这个 1M 多的内存文件里有 15 个 zip 魔数。

可以进一步把这个文件当做 zip 文件来解析,可以看到 zip 文件对应的 zip entry 有哪些。

接下来就是去找是谁在读这些 jar 包,读文件会有系统调用,于是这里 strace 就可以看看到底是怎么读的。(也可以通过 jstack 看 java 层的堆栈找到同样的原因,这里不展开)

这里出现了一个不认识的临时文件,还有一个前缀 FastClasspathScanner,去代码里搜,原理是项目用了 FastClasspathScanner 来扫描 class 文件

FastClasspathScanner 项目地址在 github.com/classgraph/… ,FastClasspathScanner 提供了一种简单快速的方法来扫描 Java 类路径。它可以轻松找到类路径上的所有类、资源、包和模块,并获取有关它们的信息。这个项目用它来做什么呢?

经过看代码,它大概是用来去 jar 包里搜哪些类实现了 com.seewo.school.statistics.counter.Counter 接口,然后去 classpath 中的找到实现了这个接口的类,也就是遍历所有的 jar 包去找实现类。

FastClasspathScanner 的做法是先把这些依赖的 jar 包先拷贝到临时目录(注意这里的 tempFile.deleteOnExit(),虽然跟此次问题不相关,但也是一个内存隐患,等下介绍)

然后读取这些临时 jar 包,

大量申请释放内存的地方在 java.util.zip.Inflater 类,调用它的 end 方法会释放 native 的内存。如果 end 方法没有调用,就会导致内存泄漏,java.util.zip.InflaterInputStream 类的 close 方法在一些场景下是不会调用 Inflater.end 方法,如下所示。

但是 Inflater 类有实现 finalize 方法,在 Inflater 对象不可达以后,JVM 会帮忙调用 Inflater 类的 finalize 方法

public class Inflater {
    public void end() {
        synchronized (zsRef) {
            long addr = zsRef.address();
            zsRef.clear();
            if (addr != 0) {
                end(addr);
                buf = null;
            }
        }
    }
    protected void finalize() {
        end();
    }
    private native static void initIDs();
    // ...
    private native static void end(long addr);
}

有几种可能性

  • Inflater 因为被其它对象引用,没能释放,导致 finalize 方法不能被调用,内存自然没法释放
  • Inflater 因为还没被 FinalizerThread 执行 fianlize 方法,导致没有释放
  • Inflater 的 finalize 方法被调用,但是被 libc 的 ptmalloc 缓存,没能真正释放回操作系统

更多关于 finalize 机制,大家可以移步笨神的文章:「JVM源码分析之警惕存在内存泄漏风险的FinalReference(增强版) 」 heapdump.cn/article/265…

于是 dump 堆内存去分析是不是有大量的 Inflater 类没有被回收,经过内存分析看,发现 java.util.zip.Inflater 类有 6k 多没有被回收。

没有被回收的原因是它们被 Finalizer 引用,需要两次 GC 才有可能被回收。

而且 FinalizerThread 的优先级比较低,如果 CPU 比较紧张的情况下,会导致需要很久才会把队列中 f 对象的 finalize 方法执行完。又因为这个时间比较长,可能导致 f 对象多次 GC 以后进到老年代,如果老年代 gc 频率不高,那 f 对象存活的时间就更久了。

这样的 native 内存短时间不释放,又由于定时任务长期执行,就可能会导致内存碎片、glibc 内存不归还的出现(等下验证),就算释放 libc 也有可能不会还给操作系统。

通过手动多次触发 GC,确认可以将所有的 java.util.zip.Inflater 回收掉,但是 natvie 内存并没有太大的变化。于是怀疑是 glibc 的内存碎片和内存没有归还给操作系统。

如何修改

有几种可能的修改方式

方案 1:其实这里明显是程序上设计不合理,没必要每次定时任务都去扫描包,这些包又不会变,扫描一次就可以了,让开发的同学去修改代码,把第一次扫描的结果缓存起来。然后打了一个包去开发环境运行,效果非常明显,新版本跑了一整天都内存几乎没有什么波动,旧版本则缓慢的上涨了 400M 左右。

方案 2:修改 FastClasspathScanner 代码,在流关闭的时候,顺带关闭 Inflater, SpringBoot 里面是这么实现的。(不想改了)

SpringBoot 里面的改动如下:github.com/spring-proj…

方案 3:前面怀疑是因为 glibc 的内存碎片,尝试替换碎片整理更友好的 tcmalloc 或者 jemalloc,看看效果。

LD_PRELOAD=/usr/local/lib/libtcmalloc.so java -jar xxx

下面是换了 tcmalloc 以后的效果,tcmalloc 贼稳。

可以看到换到了对内存碎片更友好的内存分配器以后,内存的增长得到了非常好的控制。

番外篇

上面提到 tempFile.deleteOnExit() 会有巨大的坑,通过内存 dump 的分析,可以看到 java.io.DeleteOnExitHook 占了将近 40M。

里面有一个静态的 hashset,里面存了 10 几万个字符串,就是 FastClasspathScanner 产生的临时文件路径。

是因为这里调用了 File.deleteOnExit,这个可太坑了。

它把文件的路径加到了一个 jvm 全局 DeleteOnExitHook 类的静态变量 files 中。

又因为临时文件每次的路径都是不一样的,导致这个 hashset 随着定时任务的执行逐渐变大,永远无法回收。

DeleteOnExitHook 本意是用来在 Java 虚拟机退出的时候删除文件。

对于 server 端这种长时间运行的程序,用 deleteOnExit 就太坑了,只有等容器退出那会才会执行删除。再加上这里的文件路径每次都变,导致内存白白浪费。

小结

因为程序设计的问题导致频繁读取 jar 包(实际是 zip 文件),需要调用 native 的代码去处理 zip 文件,会有非常多 native 内存分配的产生。又因为用了 zip 默认的 InflaterInputStream,导致没有办法在流关闭时调用 java.util.zip.Inflater 类的 end 方法释放 native 内存,只能等到 Finalizer 机制在多次 GC 以后调用,导致了 native 内存可能在短时间内无法释放。

又因为内存碎片和 libc 内存分配器的实现策略,导致了它没有将内存真正释放给操作系统,导致了缓慢的内存增长。

简单来说,有一个猪队友在不停的申请内存(无法立刻释放),又由于 libc 碎片化和内存二道贩子不一定会把 native 内存还给 os,导致了内存的缓慢增长。

一点想法:

  • Java 的 zip 机制是真的设计有点坑,
  • Finalize 机制完全帮倒忙,弊远大于利,新版本 Java 确实也做了修改。
Dump文件深度解析:从核心转储到内存泄漏排查的实战指南 在软件开发和系统运维中,进程崩溃或异常是常见挑战。日志记录了事件,但无法揭示深层原因。此时,Dump文件(核心转储或堆转储)成为关键的事后取证工具。其原理是操作系统或运行时环境在进程异常终止时,将内存状态、寄存器值和线程堆栈完整保存为快照文件。这项技术的核心价值在于,它允许开发者在无法复现的生产问题现场进行深度离线分析,定位诸如段错误、内存越界等底层故障。例如,在分析Java应用的内存泄漏(OOM)或排查SAP ABAP程序中的运行时异常(如涉及`ac_flush_call_internal`函数的错误)时 阅读详情

相关推荐

JVM崩溃无堆栈追踪?深度解析hs_err文件排查与修复

在Java应用开发与运维中,JVM崩溃是系统稳定性的严重威胁。当进程因致命错误退出时,常规的异常堆栈信息可能无法生成,取而代之的是hs_err日志文件。这类问题通常涉及底层内存管理、信号处理机制或本地库交互,其根源往往超出了Java异常处理范畴,指向操作系统或硬件层面。从技术原理看,JVM在崩溃前会尽力保存内存快照和线程状态,形成包含寄存器值、内存映射等信息的诊断报告。对于工程实践而言,掌握hs_err文件的分析方法至关重要,它能帮助定位JNI库兼容性、内存损坏或资源耗尽等核心问题。通过结合系统日志、监控数

weixin_30263073的博客 413

Native内存泄漏一步查

到这里,我们就能通过libmemunreachable找到泄漏的内存地址以及大小了,当然,这里的信息可能还不够,比如想获取泄漏的堆栈信息等等,这个时候就需要我们去hook 一些分配函数了,比如malloc mmap等,这里我就不给出了,emmm,有机会我会填完这个坑!如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

Eqiqi的博客 573

java jvm知识点

✅JVM = 内存 + GC + 类加载 + 调优。

a378113472的博客 179

Android 内存分析(java/native heap内存、虚拟内存、处理器内存 )

通过Byte数组和Bitmap来了解,dalvik java 堆内存、native heap 内存、32位设备3G虚拟内存,手机处理器内存等等。

HeXinGen的博客 9176

使用IDEA内置工具高效定位Java内存泄漏:从原理到实战

Java虚拟机(JVM)内存管理是保障应用稳定性的核心,内存泄漏是常见且棘手的问题,它会导致应用性能下降甚至崩溃。其原理在于对象在不再被需要后,由于意外的引用链而无法被垃圾回收器(GC)回收,从而持续占用堆内存。传统排查依赖jmap、jstat等命令行工具与MAT等独立分析器,流程割裂、效率较低。IntelliJ IDEA Ultimate版集成的内存分析工具,通过将实时监控、堆转储分析与源码导航无缝集成,极大提升了排查效率,尤其适用于开发、测试环境的在线调试与问题复现。本文将以一个典型的Spring Bo

weixin_34221276的博客 320

Java线上服务CPU与内存异常排查:从监控到代码的完整实战指南

在分布式系统与微服务架构中,性能监控与故障排查是保障服务稳定性的核心能力。其基本原理在于通过采集系统运行时数据,定位资源瓶颈与异常根因。这项技术的核心价值在于能快速恢复服务可用性,并优化资源利用率,广泛应用于高并发电商、金融交易等对稳定性要求极高的场景。本文聚焦Java应用,针对线上服务CPU使用率飙升与内存泄漏两大高频问题,提供一套系统化的诊断路径。通过整合操作系统命令、JVM内置工具及Arthas等高级诊断工具,从监控指标分析入手,逐步深入到线程堆栈与堆内存剖析,最终定位至具体代码行。文中将详细解析如何

weixin_30247307的博客 555

JVM Native内存泄露的排查分析(64M 问题)

我们有一个线上的项目,刚启动完就占用了使用 top 命令查看 RES 占用了超过 1.5G,这明显不合理,于是进行了一些分析找到了根本的原因,下面是完整的分析过程,希望对你有所帮助。会涉及到下面这些内容Linux 经典的 64M 内存问题堆内存分析、Native 内存分析的基本套路tcmalloc、jemalloc 在 native 内存分析中的使用finalize 原理hibernate 毁人不倦。

smart_an的专栏 2067

GZIP引发的JAVA Native Memory泄漏案例

在实际开发中,我们应该注意资源的正确释放,以避免类似的内存泄漏情况的发生。但是,在每次循环结束时,我们没有显式地关闭这些流对象,导致它们占用的本地内存资源无法被正确释放。在实际应用中,我们应该密切关注类似的资源使用,并确保在不再需要时及时释放它们,以保证应用程序的稳定性和性能。每次循环,我们生成一个固定长度的输入数据,然后使用GZIP进行压缩和解压缩,最后对解压缩后的数据进行处理。为了解决这个问题,我们需要在每次循环结束时,显式地关闭相应的流对象,释放占用的本地内存资源。具体来说,每次循环中,我们创建了。

学习使你进步。 317

排查JVM内存泄露问题

摘要:JVM内存泄漏排查需遵循"确认趋势→抓取快照→分析定位→修复代码"流程。核心判断标准是老年代内存使用率持续上升且Full GC后不回落。使用jstat监控内存趋势,通过jmap或Arthas获取堆快照,借助MAT工具分析支配树和GC Roots引用链。常见泄漏场景包括静态集合膨胀、ThreadLocal未清理、资源未关闭等,修复需针对性地限制集合大小、及时释放资源。对于Native内存泄漏需开启NMT跟踪。排查关键在于对比内存基线变化和追踪异常对象的引用链,建议配置GC日志实现早期预警。(149字)

ywl470812087的博客 272

Java基础高级进阶篇之Jvm详解(详解篇五十六)

本文系统介绍了JVM的核心知识点,主要包括以下内容: JVM架构概述:包括类加载子系统、运行时数据区、执行引擎和垃圾收集器等核心模块的组成与功能。 类加载机制:详细解析了类加载的七个阶段(加载、验证、准备、解析、链接、初始化、使用),重点说明了加载阶段的字节流来源、验证阶段的四种检查、准备阶段的静态变量初始化规则等关键过程。 运行时数据区:介绍了堆、方法区、虚拟机栈等内存区域的划分与作用。 执行与垃圾回收:简要提及执行引擎的工作机制和垃圾收集器的不同类型。 文章通过图示和代码示例(如类加载过程、静态变量初始

萧瑟余晖的博客 272

elasticsearch-6.5.4集群部署(图文详细)及常见错误

目录 服务器规划 部署包下载 服务器初始化(所有节点都要操作) 内核参数修改 /etc/security/limits.conf修改 /etc/sysctl.conf文件修改 禁用selinux 关闭防火墙 创建es用户 安装jdk es部署 单节点(节点1)部署 部署包上传解压 配置文件修改 修改文件属组 部署包分发 单节点(节点2)部署 配置文件修改 修改文件属组 单节点(节点3)部署 配置文件修改 修改文件属组 启动ES集群 验证 单节点验证 查看集

夜灬狼丶 1978

Tomcat假死问题深度排查:从线程死锁到内存泄漏的实战指南

在Java Web应用开发中,线程死锁和内存泄漏是导致服务不可用的两大核心问题。线程死锁通常源于不合理的同步机制或资源竞争,当多个线程相互等待对方持有的锁时,系统将陷入停滞。内存泄漏则由于对象无法被垃圾回收器释放,长期累积会引发频繁的Full GC甚至OutOfMemoryError,最终耗尽系统资源。这些问题在Tomcat这类Servlet容器中尤为突出,会直接导致应用进入“假死”状态——进程存活但服务无响应。通过系统性的排查方法,如分析线程转储(Thread Dump)定位阻塞线程,利用jstat监控G

weixin_30530939的博客 580

嵌入式开发实战:如何用cJSON库在STM32上实现轻量级JSON通信(附完整代码)

本文详细介绍了如何在STM32嵌入式系统中使用cJSON库实现高效的JSON通信方案。针对嵌入式环境的内存限制,提供了内存优化策略、cJSON库的深度定制方法以及完整的通信流程实现代码,帮助开发者解决JSON解析效率低下和跨平台兼容性等核心挑战。

star5的专栏 471

JVM内存溢出(OOM)全解析:从堆、栈到元空间的排查与实战

内存溢出(OutOfMemoryError)是Java程序运行时的常见故障,其本质是JVM无法在特定内存区域分配所需空间。理解其原理需要从JVM内存模型切入,它精细划分了堆、栈、方法区(元空间)和直接内存等区域,每个区域功能不同,溢出原因也各异。掌握内存溢出排查技术,对于保障系统稳定性和性能优化具有重要价值,尤其在微服务、高并发等应用场景中至关重要。本文聚焦于**内存泄漏**和**元空间**等核心热点的深度剖析,通过系统性的监控、诊断工具(如Heap Dump分析)和实战案例,提供从问题定位到解决方案的完整

weixin_30603633的博客 399

从原理到实现:在《图灵完备》中构建一个144*256像素、1536时钟刻/帧的视频播放器

本文详细解析了在《图灵完备》游戏中构建144*256像素、1536时钟刻/帧视频播放器的全过程。从图灵完备原理出发,通过分层架构设计、视频数据预处理和关键电路实现,展示了如何用基础电子元件模拟现代显卡功能。重点介绍了像素坐标定位系统和颜色数据处理流水线的优化方案,为游戏内复杂电子系统设计提供实践指导。

weixin_42520025的博客 445

Verilog与综合:数字逻辑的代码化革命及其未来挑战

本文深入探讨Verilog硬件描述语言与逻辑综合技术在数字芯片设计中的革命性作用。Verilog实现了数字逻辑的代码化描述,而综合技术则将其自动转换为门级网表,大幅提升设计效率。文章重点分析了在先进工艺节点下面临的时序、功耗和物理效应等挑战,并展望了AI驱动综合优化的未来发展方向。

postgres8guard的博客 802

MATLAB调用Java内存泄漏全解析:从双内存模型到根治策略

在跨语言编程与混合计算环境中,内存管理是保障应用稳定性的核心挑战。其原理涉及不同运行时环境(如MATLAB与JVM)各自独立的内存模型及垃圾回收机制,当两者通过接口交互时,若对象生命周期管理不当,极易引发隐蔽的内存泄漏。这一问题的技术价值在于,它直接关系到科学计算、工程仿真等场景下长期运行程序的可靠性与性能。典型的应用场景包括MATLAB调用Java高性能库、第三方API或遗留系统进行数据处 理与算法加速。本文聚焦于MATLAB与Java互操作时的**内存泄漏**诊断与防治,深入剖析由**静态集合**持有、

weixin_33978044的博客 751

C# 上位机内存泄漏排查指南:3 个工具 + 4 个技巧,从 GC 频繁到稳定运行 720 小时

C# 上位机内存泄漏排查的核心是「先定位泄漏存在,再追溯引用根源,最后修复验证通过「Visual Studio 诊断工具(快速定位)+ dotMemory(深度分析)+ PerfView(生产监控)」三个工具的配合,结合「复现场景、排查静态成员+事件、检查非托管资源、GC 日志验证」四个技巧,可高效解决绝大多数泄漏问题。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 735

JVM堆内存溢出深度解析:从原理到实战调优与监控告警

Java虚拟机(JVM)内存管理是保障应用稳定性的核心,其中堆内存作为对象实例的主要存储区域,其溢出问题尤为常见。其原理在于当程序申请内存时,若垃圾收集器无法在堆中找到足够的连续空间,便会抛出OutOfMemoryError。理解JVM内存模型与垃圾回收机制,对于优化应用性能、避免服务中断具有关键的技术价值。在微服务、大数据处理等高并发应用场景中,不当的内存使用极易引发问题。本文聚焦于**堆内存溢出**的根因排查,详细阐述了如何通过分析**GC日志**和使用专业工具进行诊断,并提供了从参数设置到代码实践的系

weixin_33906657的博客 410

Isaac Sim 入门教程(四)强化学习策略训练宇树人形机器人四足机器人示例

isaac_sim_policy_example Extension 是在 Isaac Sim 中部署 Isaac Lab 强化学习策略的一个框架和一组辅助函数。有关在 Isaac Sim 中训练和构建策略的详细信息,请访问在 Isaac Sim 中部署策略。该扩展程序默认已启用。如果被禁用,可在扩展管理器中搜索 isaacsim.robot.policy.example 重新启用。要运行下面的示例,请激活 “视窗”>“示例”>“机器人示例”,这将打开 “机器人示例 ”选项卡。

机器人控制与仿真应用 3926

FPGA开发中的操作系统

在FPGA开发中,操作系统起着关键的作用,它负责任务管理、资源管理、中断处理和设备驱动程序等功能。在实际的FPGA开发中,可以选择使用现有的操作系统,如MicroC/OS-II、FreeRTOS等,或者根据具体需求自行设计和开发操作系统。在FPGA(可编程逻辑门阵列)开发中,操作系统扮演着关键的角色,为硬件设计师提供了一种有效的方式来管理资源、调度任务并实现系统级功能。本文将介绍FPGA开发中的操作系统概念,并提供一个简单的示例代码来说明如何在FPGA平台上实现一个基本的操作系统。

CqpFsharp的博客 1156

后端性能问题排查实战:从可观测性到代码级深度诊断

在分布式系统与高并发场景下,性能瓶颈与偶发性故障是后端工程师面临的常见挑战。理解系统可观测性(Observability)的核心原理,是构建稳定服务的基础。通过指标(Metrics)、日志(Logs)与链路追踪(Traces)三大支柱,我们可以量化系统状态、记录事件上下文并还原请求链路,从而将模糊的性能问题转化为可分析的数据。这项技术的核心价值在于,它提供了从宏观监控到微观代码执行的统一视图,帮助工程师在复杂环境中快速定位根因。典型的应用场景包括接口响应时间飙升、CPU或内存异常、数据库慢查询以及难以复现的

快马扬鞭须努力! 428

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

跨语言通信安全漏洞图谱:三层信道风险与内存泄漏检测实战

在微服务与云原生架构中,跨语言通信已成为连接不同技术栈组件的核心枢纽。其核心原理在于通过标准化的协议(如gRPC、HTTP)或进程间通信(IPC)机制,实现数据在不同编程语言运行时环境间的交换。这一过程的技术价值在于提升了系统构建的灵活性与异构集成能力,但同时也引入了数据一致性、协议实现差异和内存管理等复杂风险。尤其在涉及C扩展、本地库交互或网络缓冲区操作时,堆外内存泄漏和文件描述符泄漏等内存安全问题变得异常隐蔽,传统单语言调试工具往往难以定位。这些风险在AI Agent通信、多语言微服务调用等应用场景中尤

weixin_33994429的博客 537

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

上一篇: 直冲云霄,阿里大牛耗时49天整理12W字面试手册,押题准确率直冲90%
下一篇: 绝 !全网居然有人一次性把Java虚拟机HotSpot 给讲透彻了
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值