1.Java对象内存布局
java对象在内存中的布局主要分为三部分,对象头,实例数据,对齐填充,其中比较重要的是对象头中的MarkWord,它里面包含了对象自身运行时的数据,包括哈希码、存活年龄、锁的状态标志、偏向线程ID、偏向时间戳;
2.JDK1.8 JVM 内存结构
分为五个区域:虚拟机栈,本地方法栈,程序计数器,堆,元空间
各个区域存储的东西:
栈:存储的都是局部变量,凡是定义在方法中的都是局部变量(方法外的是全局变量)(包括基本类型和引用类型),局部变量存储在栈中,Java中一栈默认是1M,栈是线程私有的,每个线程都有自己的栈如
int a= 10; // 局部变量a,存储在栈中
堆:存储的是数组和对象(其实数组就是对象),凡是new建立的都是在堆中,堆中存放的都是实体(对象),另外实例变量也存储在堆中,如
int a; // 实例变量a,存储在堆中
Example ex= new Example(); // ex是对象的引用,存储在栈中,
//new Example()是对象实例,存储在堆中
类对象在元空间,实例对象在堆
元空间:使用的是直接内存,一句话,跟类有关的都存储在元空间中
-
类的元数据:
- 类的结构信息:包括类名、父类、实现的接口等。
- 字段信息:类的字段名、类型、修饰符等。
- 方法信息:方法名、参数类型、返回类型、修饰符等。
- 方法字节码:方法的字节码,即 JVM 执行的方法体。
- 注解信息:类和方法上的注解信息。
- 运行时常量池:存储类的常量信息,如字符串常量、数字常量等。
-
字段和方法数据:
- 静态字段:类的静态字段及其初始值。
- 方法数据:方法的额外数据,如方法的访问标志、异常表等。
-
方法和构造函数的字节码:
- 方法字节码:方法的字节码,即 JVM 执行的方法体。
- 构造函数字节码:类的构造函数的字节码。
-
其他元数据:
- 类加载器数据:类加载器的元数据信息。
- JNI 方法:通过 JNI(Java Native Interface)注册的本地方法的元数据
5.类对象也在元空间
例如:
public class MyClass {
private int field1;
private String field2;
public void myMethod() {
System.out.println("Hello, World!");
}
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.myMethod();
}
}
//类的结构信息:MyClass 的类名、父类(Object)、实现的接口(无)。
//字段信息:field1 和 field2 的名称、类型、修饰符。
//方法信息:myMethod 和 main 方法的名称、参数类型、返回类型、修饰符。
//方法字节码:myMethod 和 main 方法的字节码。
//运行时常量池:类的常量信息,如字符串常量 "Hello, World!"。
//静态字段:类的静态字段及其初始值(本例中没有静态字段)。
特殊的string:string a =“张三” ,a在栈里边,string在元空间,“张三”也在元空间
绿色的代表线程之间是隔离的,白色的代表线程直接可以共享
以下是各个区域的解释:
1.程序计数器:线程私有的(每个线程都有一个自己的程序计数器),是一个指针.代码运行,执行命令.而每个命令都是有行号的,会使用程序计数器来记录命令执行到多少行了.记录代码执行的位置
2.本地方法栈:线程私有的(每个线程都有一个自己的本地方法栈),和Java虚拟机栈类似,Java虚拟机栈加载的是普通方法,本地方法加载的是native修饰的方法.
3.虚拟机栈:线程私有的(每个线程都有一个自己的Java虚拟机栈)( java 开启一个线程都会开启一个虚拟机栈,默认大小是1M)
只要记住跟栈相关的都是线程私有的,跟随线程创建而创建,线程死亡而死亡,生死依赖于线程;
4.堆:线程共享的(所有的线程共享一份).存放对象的,new的对象都存储在这个区域
5.元空间:线程共享,存储.class信息,类的信息,方法的定义,静态变量等.常量池放到元空间
堆和元空间都是线程共享的,在Java虚拟机中只有一个堆、一个元空间,并在JVM启动的时候就创建,JVM停止才销毁。
3.OOM可能发生在哪些区域上
OOM是指JVM的内存不够用了,同时垃圾收集器也无法提供更多的内存。从描述中可以看出,在JVM抛出OutOfMemoryError之前,垃圾收集器一般会出马先尝试回收内存。
第一,堆内存。堆内存不足是最常见的发送OOM的原因之一
第二,元空间。随着Metaspace元数据区的引入,方法区的OOM错误信息也变成了
而Java虚拟机栈和本地方法栈会出现栈溢出
4.堆内存结构
堆内存主要分为老年代和年轻代,其中年轻代又分为s0,s1,Eden;新建的对象都在年轻代,当垃圾回收次数达到15次的时候,就会将它从年轻代放入老年代,代表这个使用的频次较高;
堆内存:年轻代
老年代
问:为什么年轻代向老年代转变的时候,垃圾回收次数最高15次就要向老年代转变?
因为markWord中年龄计数最大为4bit,而4bit转换成二进制最大也就1111,对应的就是15;
新生代发生的GC也叫做MinorGC,MinorGC发生频率比较高,老年代的GC叫做MajorGC【meɪdʒə(r)】即FullGC,FullGC发生频率比较低,老年代对象存活时间比较长,存活率比较高。
思维导图:
语雀链接:https://www.yuque.com/winksvv/fkbrfo/ddcw0zwt13lfzzv5?singleDoc# 《JVM相关知识》
本地内存和直接内存的关系
本地内存:指操作系统管理的内存(也就是你购买电脑时候的运行内存),它不直接受JVM管理,但JVM运行时会使用这部分内存来存储一些信息。
直接内存:直接内存是操作系统开辟出来的一块供程序和操作系统共同使用的一块内存,这样避免线程频繁地在程序和操作系统之间切换,归属于本地内存
5、Java的垃圾回收器
JDK8默认采用的PS(年轻代)/PO(老年代)的垃圾回收器
G1优点,既可以处理年轻代又可以处理老年代,并且他把内存抽象为一个一个的区,这样可以根据每次清除区的多少控制STW时间
6、JVM故障诊断工具
基础故障处理工具:
jps:列出当前系统中所有Java进程的进程ID(PID)和主类名
jstat:监控JVM的垃圾收集(GC)、类加载、内存等信息
jinfo:显示或配置JVM的配置参数;使用 jps 的-v
选项可以查看 JVM 启动时显式指定的参数,而 jinfo 的-flag
选项可以帮助我们查看那些使用了默认值的参数。更强大的是,jinfo 甚至可以在运行时修改这些参数。
jmap:内存映像工具,生成一个内存的快照,dump文件
jhat:虚拟机堆转储快照分析工具,用来解析jmap生成的快照,查看堆内存使用情况
jstack:JVM中所有线程的堆栈跟踪信息
开发或测试环境使用JConsole或VisualVM
JConsole
可以监控JVM资源,监控内存(堆内存,非堆内存),线程(最大线程数,存活线程数,线程状态,死锁),类
VisualVM
功能相对于JConsole强一些,更详细,可以通过可视化页面查看系统资源占用情况,线程的相关信息,以及堆的信息通过动态的视图展现出来
问题:你们如何对Java程序优化?
通过top和ps命令(top命令动态的查看进程的信息,可以看出进程的cpu利用率,ps是静态的,生成一个进程信息的快照,比top更加详细)去查看我们的慢进程,这里可以看到pid,当然可以用JDK自带的这些命令(jps、jinfo、jstat、jmap、jhat、jstack),如果是开发或测试环境就用JConsole或VisualVM,如果是线上就用Arthas,线上的排查都是我们老大在做的,我在旁边看到过他使用dashboard(实时显示当前系统的运行状况,包括线程信息、内存使用情况、GC统计等)和jvm(展示目标JVM的基本信息,如JVM启动参数、系统属性、类加载器统计数据、垃圾收集器类型等)命令
7、Arthas介绍
官网:arthas
Arthas 是一款由阿里巴巴开发并开源的 Java 应用诊断工具,主要用于帮助开发人员实时监控、诊断和调优 Java 应用程序。
特点:
-
实时性:可以在不重启应用的情况下进行诊断,这对于生产环境的故障排查尤为重要。
-
命令行界面:采用命令行交互模式,提供丰富的 Tab 自动补全功能,使得操作更加便捷。
-
广泛的兼容性:支持 JDK 6 及以上版本,并且可以在 Linux、Mac 和 Windows 等操作系统上运行。
-
强大的功能集:
线程分析:查看和分析线程堆栈,找出死锁或瓶颈。
内存分析:监控和分析内存使用情况,查找内存泄漏。
GC 分析:监控垃圾回收行为,优化 GC 参数。
类加载分析:检查类加载路径,确认类是从哪个 JAR 文件加载的。
方法跟踪:动态跟踪方法调用,了解方法的执行路径和耗时。
SQL 调用监控:在没有源码的情况下监控数据库调用。
MBean 接口调用:利用 MBean 接口获取系统信息。
火焰图生成:通过执行本地命令来生成火焰图,进行性能分析。
Arthas的两个常用命令
dashboard:过输入dashboard
命令,可以获取包含线程状态、CPU占用率、内存使用情况(堆/非堆内存)、GC活动、操作系统信息及Java版本等关键指标的综合视图
jvm:jvm
命令专用于全面输出JVM运行时信息,包括虚拟机参数(如堆内存配置、垃圾回收器类型)、环境变量、系统属性等核心数据。