Java GC 监控, jstat

java gc监控, jstat jstat -<option> [-t] [-h] <vmid> [<interval> [<count>]] jstat -gc -h3 <jvm PID> 3000 -1 # -gc 显示gc的信息,查看gc的次数,及时间。 # -h: 即-h跟数字,代表隔几行显示标题 # interval: 毫秒,代表监控间隔时间段,默认毫秒做单位 # count: 代表取数次数 Jstat在分析java的内存GC时的应用 jstat可以查看堆内各个部分的使用量,以及加载类的数量。使用时,需加上查看进程的进程id,和所选参数。 参数: jstat -class pid: Statistics on the behavior of the class loader. 显示加载class的数量,及所占空间等信息; jstat -compiler pid: Statistics of the behavior of the HotSpot Just-in-Time compiler.显示VM实时编译的数量等信息; jstat -gc pid: Statistics of the behavior of the garbage collected heap.可以显示gc的信息,查看gc的次数,及时间。其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。 jstat -gccapacity:可以显示,VM内存中三代 (young,old,perm) 对象的使用和占用大小,如: PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推, OC是old内纯的占用量。 ...

Created 9096-08-09 · Updated 9096-10-09 · 2 min · 353 words · -

jinfo

jinfo jinfo可以输出并修改运行时的java 进程的opts。用处比较简单,用于输出JAVA系统参数及命令行参数。用法是jinfo -opt pid 如: 查看2788的MaxPerm大小可以用 jinfo -flag MaxPermSize 2788 jinfo -flag MaxHeapSize 13112 <no option> 打印命令行标识参数和系统属性键值对。 -flag name 打印指定的命令行标识参数的名称和值。 -flag [+|-]name 启用或禁用指定的boolean类型的命令行标识参数。 -flag name=value 为给定的命令行标识参数设置指定的值。 -flags 成对打印传递给JVM的命令行标识参数。 -sysprops 以键值对形式打印Java系统属性。 -h 打印帮助信息。 -help 打印帮助信息。 http://www.softown.cn/post/182.html 维护记录 时间 修改内容 原因 2026-10-09 修复本地 Hugo 构建的 Raw HTML 警告:代码/配置放入代码块,正文中的尖括号占位符改为行内代码;文件重命名为 jinfo.md;url 改为 jinfo;旧 url 加入 aliases;categories 改为 Java 正文中的 HTML/XML 片段被当作原始 HTML 丢弃;文件名/URL/标题按规范调整

Created 11116-11-11 · Updated 9096-10-09 · 1 min · 60 words · -

Class 文件内容, 方法区, 常量池

Class 文件内容, 方法区, 常量池 当JVM运行Java程序的时候,它会加载对应的 class文件,并提取class文件中的信息存放在JVM开辟出来的方法区内存中。那么这个 class 文件里面到底有些什么内容呢? 一、class文件内容概述 class文件是由8bits的字节流组成,全部字节构成了15个有意义的项目。这些项目之间没有任何无意义的字节,因此class文件非常紧凑。占据多字节空间的项目按照高位在前的顺序存放。下面我们详细讨论这些项目: magic (魔数) 每个class文件的前4个字节称为魔数,值为 0xCAFEBABE。作用在于轻松的辨别class文件与非class文件。 minor_version、major_version(次、主版本号) 各占2个字节。随着Java技术的发展,class文件的格式会发生变化。版本号的作用在于使得虚拟机能够认识当前加载class的文件格式。从而准确的提取class文件信息。 constant_pool_count 、constance_pool (常量池) 从这里开始的字节组成了常量池 。 存储了诸如符号常量、final常量值、基本数据类型的字面值等内容。JVM会将每一个常量构成一个常量表,每个常量表都有自己的入口地址。而实际上在JVM会将这些常量表存储在方法区中一块连续的内存空间中,因此class文件会根据常量表在常量池中的位置对其进行索引。比如常量池中的第一个常量表的索引值就是1,第二个就是2。有的时候常量表A需要常量表B的内容,则在常量表A中会存储常量表B的索引值x。而 constant_pool_count 就记录了有多少个常量表,或者所有多少个索引值。实际上,常量池中没有索引值为0的常量表,但这缺失的索引值也被记录在constant_pool_count中,因此 constant_pool_count等于常量表的数量加1。关于常量池的具体内容,我们会在下面详细讲述,并用一个例子来显示整个class文件的内容。 access_flags(访问标志) 占用2个字节。用来表明该class文件中定义的是类还是接口,访问修饰符是public还是缺省。类或接口是否是抽象的。类是否是final的。 this_class 占用2个字节。 它是一个对常量池的索引。指向的是常量池中存储类名符号引用的CONSTANT_Class_info常量表(见下面常量池具体结构)。比如this_class=0x0001。则表示指向常量池中的第一个常量表。通常这个表是指向当前class文件所定义的类名。 super_class 占用2个字节 与this_class类似,指向存放当前class文件所定义类的超类名字的索引的CONSTANT_Class_info常量表。 inteface_count、interfaces interface_count 是class文件所定义的类直接实现的接口或父类实现的接口的数量。占2个字节。intefaces包含了对每个接口的 CONSTANT_Class_info常量表的索引。 fields_count、fields fields_count 表明了类中字段的数量 。fields是不同长度的field_info表的序列。这些field_info表中并不包含超类或父接口继承而来的字段。field_info表展示了一个字段的信息,包括字段的名字,描述符和修饰符。如果该字段是final的,那么还会展示其常量值。注意,这些信息有些存放在field_info里面,有些则存放在field_info所指向的常量池中。下面我们阐述一下这个field_info表的格式: access_flags(2byte 访问修饰符) name_index(2byte 存储字段名的常量表在常量池中的索引) description_index(2byte 存储字段的所属类型的常量表在常量池中的索引) attribute_count(2byte 属性表的数量) attribute (属性) 其中attribute是由多个attribute_info组成。而JVM规范定义了字段的三种属性: ConstanceValue、Deprecated和Synthetic。 method_count、methods 与字段类似,method_count表明类中方法的数量和每个方法的常量表的索引。methods表明了不同长度的method_info表的序列。该表格式如下: access_flags(2byte 访问修饰符) name_index(2byte 存储方法名的常量表在常量池中的索引) description_index(2byte 存储方法的返回类型和参数类型的常量表在常量池中的索引) attribute_count(2byte 属性表的数量) attribute (属性) 其中方法的属性JVM规定了四种: Code,Deprecated,Exceptions,Synthetic。 常量池的具体结构 在Java程序中,有很多的东西是永恒的,不会在运行过程中变化。比如一个类的名字,一个类字段的名字/所属类型,一个类方法的名字/返回类型/参数名与所属类型,一个常量,还有在程序中出现的大量的字面值。比如下面小段源码红色显示的东西。 public class ClassTest { ...

Created 15156-04-15 · Updated 9096-10-09 · 7 min · 1388 words · -

JVM 内存调优, OutOfMemoryError, StackOverflowError

OutOfMemoryError, StackOverflowError 这是IGT笔试的题目,分别写出可以造成两种错误的代码。由于之前测试过HashMap的容量的时候出现过内存不足的错误,所以能写出来,但StackOverflowError没什么印象了。 回来查了下StackOverflowError的资料,在德问社区上有人发帖子java产生StackOverflowError的原因是什么?询问: 请问java.lang.StackOverflowError产生的原因有什么? 已知的有死循环,循环的嵌套层数达到了环境的设定值 谢谢 有人回复说: 死循环本身是不会StackOverflow的,只有无限递归的时候会出现。原则上循环嵌套次数本身是没有限制的,限制的是占用的栈空间,如果你的函数里定义了很多很多变量,栈空间就会用完得比较快。 还有人讲的更详细: JVM里会有两种StackOverflowError, 一种是对应JVM stack, 一种是对应Native Method stack. 我们一般说的都是JVM stack. 每一个JVM线程维护自己的JVM stack. JVM stack里面存放 JVM栈帧. 栈帧中存放 数据和中间结果(本地变量数组, 操作符栈, 和对runtime 常量池的引用). 这些数据都比较小(对象都在堆中, 栈帧仅存放对象引用), 所以想单纯通过 在栈帧中存放大数据的方法 去引入StackOverflowError, 基本是不现实的.一般都是因为方法调用嵌套层数过大. http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.5.2 JVM stack的大小是可以调节的, sun的windows jvm6 x64,jvm栈默认大小为1024k.可以通过-Xss1024k来调节. http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#threads_oom 另外,在oracle的docsChapter 2. The Structure of the Java Virtual Machine中,有如下解释: Java Virtual Machine Stacks The following exceptional conditions are associated with Java Virtual Machine stacks: If the computation in a thread requires a larger Java Virtual Machine stack than is permitted, the Java Virtual Machine throws a StackOverflowError. ...

Created 9096-04-09 · Updated 9096-10-09 · 2 min · 407 words · -

Java Object Layout(jol)

Java Object Layout(jol) http://bboniao.com/openjdk/2014-06/java-object-layoutjol.htm http://openjdk.java.net/projects/code-tools/jol/ hg clone http://hg.openjdk.java.net/code-tools/jol/ jol cd jol mvn clean install pom.xml添加依赖 <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>1.0-SNAPSHOT</version> </dependency> 代码 public static void main(String[] args) { int size = 10; List<Integer> list = new ArrayList<Integer>(size); for (int i = 0; i < size; i++) { list.add(i); } //虚拟机信息 out.println(VMSupport.vmDetails()); //打印类内部的占用 out.println(ClassLayout.parseClass(ArrayList.class).toPrintable()); //打印实例内部的占用 out.println(ClassLayout.parseClass(ArrayList.class).toPrintable(list)); //打印实例外部的占用 out.println(GraphLayout.parseInstance(list).toPrintable()); //打印实例各个依赖的占用,并汇总 out.println(GraphLayout.parseInstance(list).toFootprint()); } 输出 ...

Created 21216-05-21 · Updated 9096-10-09 · 2 min · 399 words · -

jmap

jmap 、基本参数: -dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件. -finalizerinfo 打印正等候回收的对象的信息. -heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况. -histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀"*". 如果live子参数加上后,只统计活的对象数量. -permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来. -F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效. -h | -help 打印辅助信息 -J 传递参数给jmap启动的jvm. pid 需要被打印配相信息的java进程id,创业与打工的区别 - 博文预览,可以用jps查问. 4、使用示例 1)、[fenglb@ccbu-156-5 ~]$ jmap -histo 4939 sudo ./jmap -dump:format=b,file=/tmp/917dump.dat 8949 If you take a look at the source code for com.sun.tools.hat.internal.parser.Reader, you'll see that it's looking for the magic number 0x4a415641. This value is used to help identify valid heap dump files. jmap should append this value as the first four bytes of any heap dump file it creates. http://stackoverflow.com/questions/15507047/jhat-throwing-unrecognized-magic-number http://blog.csdn.net/gtuu0123/article/details/6039474 维护记录 时间 修改内容 原因 2026-10-09 修复本地 Hugo 构建的 Raw HTML 警告:代码/配置放入代码块,正文中的尖括号占位符改为行内代码;categories 改为 Java 正文中的 HTML/XML 片段被当作原始 HTML 丢弃;文件名/URL/标题按规范调整

Created 17176-09-17 · Updated 9096-10-09 · 1 min · 111 words · -

NMT, Native Memory Tracking

NMT, Native Memory Tracking NMT for Hotspot VM is turned off by default. Turn this feature on using JVM command line option -XX:NativeMemoryTracking=[off | summary | detail] 注意,根据Java官方文档,开启NMT会有5%-10%的性能损耗; 访问NMT数据 JDK提供了jcmd命令来访问NMT数据: jcmd <pid> VM.native_memory [summary | detail | baseline | summary.diff | detail.diff | shutdown] [scale= KB | MB | GB] 配置项 说明 summary 只打印打印按分类汇总的内存 detail 打印按分类汇总的内存用法、virtual memory map和每次内存分配调用 baseline 创建内存快照,以比较不同时间的内存差异 summary.diff 打印自上次baseline到现在的内存差异,显示汇总信息 detail.diff 打印自上次baseline到现在的内存差异, 显示详细信息 shutdown 关闭NMT功能,NMT can be shutdown using jcmd utility, but can not start/restart using jcmd ...

Created 6066-02-06 · Updated 9096-10-09 · 1 min · 110 words · -

Retained Heap

Retained Heap http://supercharles888.blog.51cto.com/609344/1347144 为了开始研究,我们希望在memory 溢出时候能自动生成heap dump文件,为此,我们在运行时候添加JVM 参数: -XX:+HeapDumpOnOutOfMemoryError 然后,我们来做一系列实验来逐步研究各个有趣的问题。 实验1: 我们先来研究最简单的memory leak例子。我们先构造一个POJO类Person,这个Person类就是一般的java 类,然后我们构造一个ArrayList,然后在一个无限循环中一直放这个Person类的实例,因为Person类和ArrayList都在堆上,而ArrayList因为是强引用,所以无法被GC回收, (因为我们List一直在用并没有摧毁) 所以一旦ArrayList所占用的堆内存填满整个heap size时候,heap就溢出了。 POJO类代码是: package com.charles.research; /** * * 这是一个Person类,我用它来撑满heap * @author charles.wang * */ public class Person { private String name; private String sex; private int age; public Person( String name,String sex,int age){ this.name=name; this.sex=sex; this.age=age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } 然后我们创建ArrayList,并且无限向其中添加Person类对象的方法是: ...

Created 7076-02-07 · Updated 9096-10-09 · 2 min · 416 words · -

Java jps

java jps jps位于jdk的bin目录下,其作用是显示当前系统的java进程情况,及其id号。 jps相当于Solaris进程工具ps。不象"pgrep java"或"ps -ef grep java",jps并不使用应用程序名来查找JVM实例。因此,它查找所有的Java应用程序,包括即使没有使用java执行体的那种 (例如,定制的启动 器) 。另外,jps仅查找当前用户的Java进程,而不是当前系统中的所有进程。 -h: help -l: 输出应用程序main class的完整package名 或者 应用程序的jar文件完整路径名 -m: 输出传递给main 方法的参数,在嵌入式jvm上可能是null, 在这里,在启动main方法的时候,我给String[] args传递两个参数。hollis,chuang,执行jsp -m: -v: 输出传递给JVM的参数 在这里,在启动main方法的时候,我给jvm传递一个参数: -Dfile.encoding=UTF-8,执行jps -v: 位置 我们知道,很多Java命令都在jdk的JAVA_HOME/bin/目录下面,jps也不例外,他就在bin目录下,所以,他是java自带的一个命令。 功能 jps(Java Virtual Machine Process Status Tool)是JDK 1.5提供的一个显示当前所有java进程pid的命令,简单实用,非常适合在linux/unix平台上简单察看当前java进程的一些简单情况。 原理 jdk中的jps命令可以显示当前运行的java进程以及相关参数,它的实现机制如下: java程序在启动以后,会在java.io.tmpdir指定的目录下,就是临时文件夹里,生成一个类似于hsperfdata_User的文件夹,这个文件夹里 (在Linux中为/tmp/hsperfdata_{userName}/) ,有几个文件,名字就是java进程的pid,因此列出当前运行的java进程,只是把这个目录里的文件名列一下而已。 至于系统的参数什么,就可以解析这几个文件获得。 hollis@hos:/tmp/hsperfdata_hollis$ pwd /tmp/hsperfdata_hollis hollis@hos:/tmp/hsperfdata_hollis$ ll total 48 drwxr-xr-x 2 hollis hollis 4096 4月 16 10:54 ./ drwxrwxrwt 7 root root 12288 4月 16 10:56 ../ ...

Created 5056-05-05 · Updated 9096-10-09 · 2 min · 243 words · -

On-Heap and Off-Heap Memory 堆内内存, 堆外内存

堆内内存, 堆外内存 一、什么是堆外内存 堆内内存 (on-heap memory) 回顾 堆外内存和堆内内存是相对的二个概念,其中堆内内存是我们平常工作中接触比较多的,我们在jvm参数中只要使用-Xms,-Xmx等参数就可以设置堆的大小和最大值,理解jvm的堆还需要知道下面这个公式: 堆内内存 = 新生代+老年代+持久代 如下面的图所示: Paste_Image.png 在使用堆内内存 (on-heap memory) 的时候,完全遵守JVM虚拟机的内存管理机制,采用垃圾回收器 (GC) 统一进行内存管理,GC会在某些特定的时间点进行一次彻底回收,也就是Full GC,GC会对所有分配的堆内内存进行扫描,在这个过程中会对JAVA应用程序的性能造成一定影响,还可能会产生Stop The World。 常见的垃圾回收算法主要有: 引用计数器法 (Reference Counting) 标记清除法 (Mark-Sweep) 复制算法 (Coping) 标记压缩法 (Mark-Compact) 分代算法 (Generational Collecting) 分区算法 (Region) 注: 在这里我们不对各个算法进行深入介绍,感兴趣的同学可以关注我的下一篇关于垃圾回收算法的介绍分享。 堆外内存 (off-heap memory) 介绍 和堆内内存相对应,堆外内存就是把内存对象分配在Java虚拟机的堆以外的内存,这些内存直接受操作系统管理 (而不是虚拟机) ,这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。 作为JAVA开发者我们经常用java.nio.DirectByteBuffer对象进行堆外内存的管理和使用,它会在对象创建的时候就分配堆外内存。 DirectByteBuffer类是在Java Heap外分配内存,对堆外内存的申请主要是通过成员变量unsafe来操作,下面介绍构造方法 DirectByteBuffer(int cap) { super(-1, 0, cap, cap); //内存是否按页分配对齐 boolean pa = VM.isDirectMemoryPageAligned(); //获取每页内存大小 int ps = Bits.pageSize(); //分配内存的大小,如果是按页对齐方式,需要再加一页内存的容量 long size = Math.max(1L, (long)cap + (pa ? ps : 0)); //用Bits类保存总分配内存(按页分配)的大小和实际内存的大小 Bits.reserveMemory(size, cap); long base = 0; try { //在堆外内存的基地址,指定内存大小 base = unsafe.allocateMemory(size); } catch (OutOfMemoryError x) { Bits.unreserveMemory(size, cap); throw x; } unsafe.setMemory(base, size, (byte) 0); //计算堆外内存的基地址 if (pa && (base % ps != 0)) { // Round up to page boundary address = base + ps - (base & (ps - 1)); } else { address = base; } cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); att = null; } 注: 在Cleaner 内部中通过一个列表,维护了一个针对每一个 directBuffer 的一个回收堆外内存的 线程对象(Runnable),回收操作是发生在 Cleaner 的 clean() 方法中。 ...

Created 17176-05-17 · Updated 9096-10-09 · 3 min · 500 words · -

Java Knowledge Map

背景 Java 相关文章分散在多篇文章中。本文用 关系图 + 索引表 串起 JVM 运行时、类加载、调试、字节码织入、生产诊断等概念,便于从一点跳到专题文。 与 BTrace 对话中沉淀的 attach / agent / ASM 链路,是下图右侧「生产诊断」分支的核心。 总览关系图 flowchart TB subgraph runtime [JVM 运行时] HS[HotSpot 简介] JVM[jvm 通用概念] CL[classloader] GC[java-gc 等] end subgraph native [Native 与通道] JVMTI[JVMTI] ATT[Attach API] JCMD[jcmd] end subgraph debug [调试与开发热更] JPDA[JPDA 远程调试] HOT[开发期热替换 DCEVM] SBDT[Spring Boot DevTools] end subgraph instrument [字节码与 Agent] INST[java.lang.instrument] ASM[java-asm] end subgraph prod [生产诊断] BTRACE[BTrace] TOOL[诊断工具选型] JFR[JFR] end HS --> CL HS --> GC HS --> ATT HS --> JVMTI JPDA --> JVMTI ATT --> INST INST --> ASM ASM --> BTRACE ATT --> BTRACE JCMD --> ATT HOT --> JPDA HOT --> ASM BTRACE --> TOOL JFR --> TOOL SBDT -.->|非 JVM HotSwap| CL 按主题索引 JVM 与 HotSpot 概念 文章 JVM 生态、实现选型 Java 虚拟机生态与选型 HotSpot 架构与站内索引 HotSpot 简介 HotSpot 启动参数 hotspot-options JIT / C1·C2 / 分层编译 hotspot-jit GC、分代(多篇) java-gc 等 Safepoint safepoint 类加载 概念 文章 双亲委托、命名空间、Agent 与业务 ClassLoader java classloader java.lang.Class(类型元信息、获取方式) lang-class Class.forName class-forname 调试体系(JPDA) 概念 文章 JPDA 三层 JDI / JDWP / JVMTI、远程调试 JAVA 调试与 JPDA JVMTI native 接口 JVMTI Attach 与内置诊断 概念 文章 Attach API、loadAgent、与 jcmd 区别 Java Attach API jcmd、Diagnostic Command jcmd 字节码、Agent、织入 概念 文章 ASM、Instrumentation、retransform、INVOKESTATIC 示意 Java ASM 与运行时字节码织入 BTrace 安装、attach、脚本、慢调用 BTrace 生产诊断工具选型(BTrace / Arthas / JFR) 生产环境诊断工具选型 JFR Java Flight Recorder 开发期热替换(非 BTrace) 概念 文章 HotSwap、DCEVM、HotSwapAgent 开发期热替换 Spring Framework 总览与演变 Spring Spring Boot DevTools Restart Spring Boot DevTools Spring Boot 总览 Spring Boot Spring IoC / 依赖注入 Spring IoC Spring Bean 生命周期 Spring Bean Lifecycle Spring WebFlux Spring WebFlux Java Virtual Threads Java Virtual Threads Spring Boot 3.x 迁移 Spring Boot 3.x Migration IDE / DCEVM 热更 vs attach 探针 见 java-asm、BTrace AOP / 其它织入 概念 文章 AspectJ 编译时织入 AspectJ CTW CGLIB、AOP cglib、aop BTrace 相关概念链(精读路径) 若从 BTrace 出发,建议按因果顺序阅读: ...

Created 31316-05-31 · Updated 11116-08-11 · 3 min · 470 words · -

2026 W29

从 HashMap 关联到的 上周说面试复盘整理了一部分,这周继续。blog 里 Java 相关的文档以前记得有些零散,重新整理一下。 HashMap 关联的东西其实挺多的… 先是解决哈希冲突主要的两条路:开链法和开放地址法。链表长到一定程度会转成红黑树,于是又去看了红黑树。哈希值怎么来的又牵出随机数——xorshift、LCG。再往下是 CPU 层面的事:加法/乘法/除法消耗的时钟周期不一样,缓存的空间局部性会直接影响这类数据结构的实际性能。横向看,Python 的 dict、Go 的 map 用的 Swiss Table,思路各不相同。 Swiss Table 这条又牵出 SIMD,还挺有意思的。1997 年的 Pentium MMX 就带了 MMX 指令集,不过只能做整数;AMD 1998 年 K6-2 上的 3DNow! 把单精度浮点加了进来;到 1999 年 Pentium III 的 SSE,才有了独立的 128 位寄存器。早年这套指令主要是拿来做多媒体和图形的。 而 Swiss Table 用 SIMD 一次比对一组控制字节来定位槽位,Go 的 map 后来换成了这个结构——中间隔了二十多年。一个 90 年代末为了跑多媒体加进来的特性,现在被用来加速哈希表的查找。 JVM 这边也一并整理了:HotSpot 的 GC 和 JIT。中间还跑偏去看了下 Linux 的进程调度。 starlabrys:把公司的壳先搭起来 和朋友聊天,有人找到了工作但不太理想,有人还在面试。我之前注册了营业执照,就在想我们是不是可以一起做点什么。 有人建议建一个 GitHub organization,这样想贡献代码的人可以加进来。公司暂时没业务,但有件事是现在就能做的——把官网建起来。 于是注册了域名 starlabrys.com,建了 GitHub org,把仓库和权限用 OpenTofu 管起来,后来又把这部分 IaC 从我自己的配置仓库拆出去,单独放进了 starlabrys-infra。 ...

Created 19196-07-19 · Updated 19196-07-19 · 1 min · 126 words · -

JDK Tools: 命令行工具现状与过时工具

JDK 自带的命令行工具随着版本演进变化很大:CORBA、JAX-WS/JAXB、Java Web Start 等整套技术栈在 JDK 11 前后被移除,一批老工具也随之消失;同时 jshell、jlink、jpackage、jhsdb 等是后来才加入的。本文按「现在还在用」「名存实亡」「已从 JDK 移除」三类整理,方便判断一个工具在 JDK 26 上还值不值得用。 仍在用的核心工具 分类 工具 用途 编译与运行 java 运行 class/jar,也可直接运行单文件源码 编译与运行 javac 编译 Java 源码 编译与运行 jshell REPL,交互式执行 Java 代码(JDK 9 引入) 打包与模块 jar 打包/解包 jar 文件 打包与模块 jlink 生成裁剪后的自定义运行时镜像(JDK 9 引入) 打包与模块 jmod 创建和查看 jmod 模块文件(JDK 9 引入) 打包与模块 jpackage 生成平台原生安装包(JDK 14 引入,见下文 javapackager 的替代) 签名与安全 jarsigner jar 签名与验证 签名与安全 keytool 密钥、证书管理 文档与静态分析 javadoc 生成 API 文档 文档与静态分析 javap 反汇编 class 文件 文档与静态分析 jdeps 分析类/模块依赖关系(JDK 8 引入) 文档与静态分析 jdeprscan 扫描代码中用到的已废弃 API(JDK 9 引入) 文档与静态分析 jnativescan 扫描 FFM API(java.lang.foreign)受限方法调用(JDK 22 引入) 诊断与监控 jcmd 向运行中的 JVM 发送诊断命令,官方现在推荐的统一入口,详见 jcmd 诊断与监控 jconsole 图形化 JMX 监控 诊断与监控 jfr Flight Recorder 命令行工具,详见 Java Flight Recorder 诊断与监控 jhsdb HotSpot Serviceability Agent 统一入口,整合了旧版 jsadebugd/SA 调试功能(JDK 9 引入) 诊断与监控 jps 列出本机 JVM 进程 诊断与监控 jstat / jstatd JVM 统计信息采集 / 远程统计信息服务 诊断与监控 jwebserver 内置的简单静态文件 HTTP 服务器(JDK 18 引入) 调试 jdb 命令行调试器 RMI rmiregistry RMI 远程对象注册表 脚本 jrunscript 命令行脚本外壳工具,见下文「名存实亡」 其它签名工具 serialver 生成 serialVersionUID Windows 专属 javaw 不弹控制台窗口运行 Java 程序 Windows 专属 jabswitch / jaccessinspector / jaccesswalker Java Access Bridge 开关与辅助功能检查工具 Kerberos kinit / klist / ktab Kerberos 票据与密钥表管理 名存实亡:还在但不建议依赖 这几个工具没有被移除,仍出现在最新 JDK 的工具列表里,但官方已经不建议把它们当作首选: ...

Created 6066-02-06 · Updated 16166-07-16 · 3 min · 475 words · -

VisualVM: 线程与堆内存监控分析工具

VisualVM 是什么 VisualVM 是一个图形化的 JVM 监控与分析工具,可以查看应用程序的 CPU、内存占用,监控线程状态,生成线程转储(Thread Dump)或堆转储(Heap Dump),跟踪内存泄漏,监控垃圾回收,并支持简单的 CPU/内存 profiling。它也能浏览和操作 MBean,配合 JMX 做远程监控。 VisualVM 曾在 JDK 6 Update 7 之后随 Sun/Oracle JDK 一起分发;从 JDK 9 起被移出 JDK 发行包,独立成开源项目 visualvm.github.io,需要单独下载安装,本身要求 JDK 8 及以上运行,但可以监控 JDK 1.4 以上版本的目标进程。 安装与启动 从 visualvm.github.io 下载对应平台的安装包,解压后运行 visualvm(Windows 下是 visualvm.exe)。 如果 VisualVM 找不到本机 JDK,用 -jdkhome 显式指定: visualvm -jdkhome /path/to/jdk 插件默认安装在用户目录下,例如 Windows: %APPDATA%\VisualVM\<version> 线程监控与 Thread Dump 启动 VisualVM 后,“应用程序 > 本地” 节点会列出本机正在运行的 Java 进程。双击目标进程,切换到「线程」标签页,可以看到各线程的实时状态跟踪。 点击「线程 Dump」按钮会生成当前时刻的线程转储文件,里面包含每个线程的调用栈、锁等待对象等信息,用来定位线程阻塞、死锁的位置;关于线程转储的字段含义与死锁排查方法,参考 jstack。 VisualVM 线程状态 状态 说明 Running 正在运行 / 可运行(runnable) Sleeping 线程调用了 Thread.sleep() Wait 线程处于 wait 或 timed_waiting Park 线程调用了 LockSupport.park() Monitor 线程被 monitor 锁阻塞(blocked) 远程监控 VisualVM 也可以通过 JMX 或 jstatd 监控远程 JVM 进程,思路和本地类似,多了一步网络连接配置。 ...

Created 4046-11-04 · Updated 16166-07-16 · 2 min · 245 words · -

jstack

背景 jstack 用来查看某个 Java 进程内的线程堆栈信息,语法: jstack [option] PID jstack [option] executable core jstack [option] [server-id@]remote-hostname-or-ip 常用参数: 参数 说明 -l long listing,额外打印锁信息;发生死锁时可用 jstack -l pid 观察锁持有情况 -m mixed mode,同时输出 Java 堆栈和 C/C++(Native 方法)堆栈 -F 当 jstack [-l] pid 无响应时强制 dump 现代 JDK(含 JDK 26)里还有替代吗 有,而且是官方推荐的替代:jcmd <pid> Thread.print。 jstack、jmap、jinfo 这几个独立小工具早已被 Oracle 标记为 experimental / unsupported,官方故障排查文档现在统一推荐通过 jcmd 这个诊断入口来做线程栈、堆 dump、JFR 等操作: jcmd <pid> Thread.print # equivalent to: jstack <pid> jcmd <pid> Thread.print -l # equivalent to: jstack -l <pid>, with lock info jstack 目前仍随 JDK 26 一起提供,还能正常使用,只是不建议在新脚本 / 新流程里继续依赖它。参数与用法详见 jcmd;更完整的生产诊断工具对比(Arthas、async-profiler、JFR 等)见 生产环境诊断工具选型。 ...

Created 11116-11-11 · Updated 16166-07-16 · 4 min · 759 words · -

DestroyJavaVM 与 JVM 内置线程

现象 用 jcmd <pid> Thread.print 查看 Java 进程的线程 dump 时,经常能看到几个"内置"线程,其中 DestroyJavaVM 最容易让人误以为是 bug 或线程泄漏: DestroyJavaVM:负责在程序退出时卸载 JVM 的线程,大部分时间都在等待,直到虚拟机真正关闭。 Signal Dispatcher:处理操作系统发给 JVM 的原生信号。 Finalizer:从 finalization 队列取出对象并调用其 finalize 方法。 Reference Handler:高优先级线程,负责将待处理的 Reference 加入队列,定义在 java.lang.ref.Reference。 DestroyJavaVM 为什么会"一直挂着" 这是因为大多数应用不只有 main 一个线程,还会额外创建其它线程。 所有 POJO 应用都从 main 方法开始执行。最简单的情况下,main 方法会完成所有工作:创建对象、调用方法等。一旦 main 执行完毕,JVM 会启动一个 DestroyJavaVM 线程来关闭虚拟机,这个线程会等待所有线程都执行完毕后才真正做关闭的工作,以确保你创建的线程都能跑完再销毁 JVM。 而带 GUI 的应用通常会运行一组线程:一个监听键盘、鼠标等系统事件,一个维护窗口和显示等。这类应用的 main 方法可能只是启动所需的线程后就退出了。它同样会创建 DestroyJavaVM 线程,但这时该线程要做的就是等待所有已创建的线程完成后才关闭虚拟机。 因此,任何创建了线程并依赖这些线程完成工作的应用,都会有一个 DestroyJavaVM 线程在等待它们结束。由于它做的只是 join 其他所有运行中的线程,本身不会消耗额外资源。 现代 JDK(包括 JDK 26)里还存在吗 存在。DestroyJavaVM 是 JNI Invocation API 里虚拟机启动 / 关闭机制的一部分,从早期 JDK 到当前的 HotSpot 都没有变过,线程 dump 里依然能看到这个线程名。 ...

Created 30306-03-30 · Updated 16166-07-16 · 1 min · 144 words · -

JVM Memory Layout and Tuning Notes

说明 本文是一篇较早的 JVM 调优笔记,原文涵盖的堆栈基础、GC 算法、分代模型、G1 设计目标等内容,站内已有更完整、持续维护的独立文章: GC 分代模型、回收算法、收集器与发展时间线 → Java GC HotSpot 启动参数(堆、分代比例、GC 日志、收集器选择等)→ HotSpot JVM 启动参数 堆、栈、方法区基础概念 → Java 堆栈方法区 强引用 / 软引用 / 弱引用 / 虚引用 → Java 引用类型 生产环境诊断工具(Arthas、async-profiler、JFR 等)→ Java 生产环境诊断工具选型 本文只保留原文中站内暂未覆盖的部分:对象内存布局与大小计算、一份历史调优配置示例、内存泄漏排查思路,以及关于 JVM 内存设计的一点延伸思考。 Java 对象的内存布局与大小 一个空 Object 对象在堆中的大小是 8 byte,这只是对象头的大小,不包含任何字段: Object ob = new Object(); 这行代码实际占用 4 byte + 8 byte:4 byte 是栈中保存引用所需的空间,8 byte 是堆中对象头的信息。因为所有 Java 非基本类型对象都默认继承 Object,所以任何 Java 对象的大小都必然大于 8 byte。 有了空对象的大小,可以推算其他对象的大小,例如: class NewObject { int count; boolean flag; Object ob; } 其大小为:空对象大小(8 byte)+ int(4 byte)+ boolean(1 byte)+ Object 引用(4 byte)= 17 byte。由于 JVM 对对象内存分配按 8 字节对齐,实际占用会向上取整到 24 byte。 ...

Created 14146-04-14 · Updated 16166-07-16 · 2 min · 227 words · -

HotSpot

背景 OpenJDK 发行版里默认的 Java 虚拟机实现是 HotSpot(C++)。你安装的 java 命令、容器里的 JDK 镜像,除非明确换用 OpenJ9 或 GraalVM Native Image,否则跑的就是 HotSpot。 Java 领域还有哪些 JVM,见 Java 虚拟机。 HotSpot 是什么 HotSpot 得名于 热点探测(hot spot detection):大部分时间只执行少量代码,JIT 优先编译这些热点方法。内置 解释器、C1 与 C2 两个 JIT 编译器,默认通过 分层编译 在启动速度与峰值性能之间折中。 对比 HotSpot Eclipse OpenJ9 GraalVM Native Image 典型形态 OpenJDK 默认 java IBM Semeru native-image 产物 JIT C1 + C2 自有 JIT 构建期 AOT,无运行时 JIT 常用诊断 jcmd、JFR、Attach 自有工具 与传统 JVM 排障不同 历史 HotSpot 的技术 lineage 来自 Self / Strongtalk 一脉的动态编译与热点探测。Longview Technologies(对外也称 Animorphic)1994 年成立,1997 年 2 月被 Sun 收购;收购后 Sun 在其 VM 技术上为 Java 编写更强的 JIT(C2 等),因运行时持续探测并优化热点代码而得名 HotSpot。 ...

Created 31316-05-31 · Updated 16166-07-16 · 3 min · 507 words · -

HotSpot JIT 编译器(C1/C2)

背景 OpenJDK 默认的 HotSpot 虚拟机通过 解释器 + 即时编译(JIT) 执行字节码:冷代码先解释运行,热点方法再编译为本地机器码。本文归纳 C1/C2 分工、分层编译与热点探测;JDK 21 之后的 AOT 预热见 JDK 25 变更汇总。 C1 与 C2 HotSpot 内置两个 JIT 编译器,历史上也称 Client 编译器 与 Server 编译器,对应 C1 与 C2: 编译器 特点 C1 编译快,做简单、可靠的优化 C2 编译慢,做更重的优化,会依据运行时 profile 做激进优化 二者都只编译 热点代码(多次调用的方法,或多次执行的循环体),编译前须先做热点探测。 Client/Server 两套 VM 的划分在 64 位 JDK 上已基本消失:现代 HotSpot 统一走 Server VM,并通过 分层编译 把 C1 的快速启动与 C2 的峰值性能结合起来(见下一节)。 分层编译(Tiered Compilation) 自 Java 7 起,Server VM 默认启用 分层编译(-XX:+TieredCompilation,一般无需手动开)。HotSpot 用 5 个执行级别 在解释器与两个编译器之间过渡: ...

Created 30306-03-30 · Updated 16166-07-16 · 2 min · 354 words · -

Java 虚拟机

背景 日常说的「JVM」常混用两层含义: JVM 规范(JLS/JVMS):字节码、class 文件格式、类加载语义、JNI、JPDA 等,与具体实现无关。 JVM 实现:真正把字节码跑起来的运行时,如 HotSpot、OpenJ9。 安装 java 命令得到的是 JDK 发行版(见 OpenJDK JDK 发行版;安装命令见 openjdk),其中默认捆绑的虚拟机实现几乎都是 HotSpot。本文介绍 JVM 实现(HotSpot、OpenJ9、GraalVM Native 等)的差异与选型;HotSpot 的参数与工具见 hotspot。 JDK 发行版 vs JVM 实现 Temurin、Corretto、Dragonwell 等是不同厂商打包的 OpenJDK 发行版,默认都跑 HotSpot,不是三种并列的 JVM。换 Temurin 换 Corretto,通常只是换构建源与支持方;要换虚拟机实现才看 OpenJ9(Semeru)或 GraalVM Native Image。发行版对照见 openjdk-distributions.md。 规范层:各实现共同遵守什么 无论用哪种 JVM,下列概念在规范层是共通的: 概念 说明 字节码 javac 产出 .class,由虚拟机解释或 JIT 编译后执行 类加载 加载、链接、初始化;双亲委托等模型见 classloader JNI Java 与本地代码互操作规范,见 java-jni JPDA 调试体系(JDI / JDWP / JVMTI),见 java-debug-JPDA 规范 不 规定用哪种垃圾收集器、有哪些 -XX 开关、是否提供 JFR——这些是实现细节。 主流 JVM 实现对照 实现 典型发行版 特点 适用场景 HotSpot Oracle JDK、Temurin、Corretto、Dragonwell 等 生态最全;C1/C2 JIT、G1/ZGC/Shenandoah、JFR、jcmd、Attach 默认可用 默认首选;绝大多数服务端与桌面应用 Eclipse OpenJ9 IBM Semeru 常更低内存占用、较快启动;工具链与 HotSpot 不同 容器密度高、内存敏感;需验证 agent/诊断工具兼容性 GraalVM GraalVM CE/EE 可嵌 HotSpot 作 JIT 运行时,或通过 Native Image AOT 为本地可执行文件 极致冷启动(serverless、CLI);见 GraalVM 简介、Native Image JRockit 已停止独立发展 曾用于金融低延迟;特性已并入 HotSpot 仅作历史了解,见 jrockit ┌─────────────────────────────────┐ │ Java 语言 + JVM 规范 │ └─────────────────────────────────┘ │ ┌───────────────────────────┼───────────────────────────┐ ▼ ▼ ▼ OpenJDK HotSpot Eclipse OpenJ9 GraalVM (默认 java 命令) (Semeru 等) ┌──────┴──────┐ │ │ ▼ ▼ │ │ HotSpot 运行时 Native Image ▼ ▼ (JIT) (AOT 本地二进制) jcmd / JFR / Attach 自有诊断工具 选型建议 没有明确理由时,用 HotSpot(即常见 OpenJDK 发行版)。文档、agent、APM、排障经验都围绕它积累最多。 内存或启动是瓶颈,且已验证依赖(Attach、部分 agent、GC 日志格式)在目标环境可用,可评估 OpenJ9。 冷启动极敏感(函数计算、短生命周期进程),评估 GraalVM Native Image(见 GraalVM 简介、Native Image);需接受构建期分析、反射配置、动态类加载受限等成本。 不要混用概念:GraalVM 不等于「又一个 HotSpot」;Native Image 路径下没有传统 JIT 预热,也没有完整的 -XX 调优空间。 HotSpot 执行路径(概念) HotSpot 的典型路径(细节见专文): ...

Created 19196-05-19 · Updated 15156-07-15 · 2 min · 308 words · -