类加载:类源代码经编译器编译为二进制字节码,通过类加载器加载到JVM
JVM内存:类存放方法区,实例对象存放堆中,方法调用时用到虚拟机栈、程序计数器、本地方法栈
执行引擎:解释器逐行解释执行方法代码,热点代码由JIT做编译、优化后的执行,GC负责回收堆中不再被引动的对象,当JAVA代码无法实现某些功能,需要使用底层操作系统功能,则需要调用本地(native)方法接口使用操作系统的功能方法
问题:代码是如何被翻译执行?
ANS:java源代码(.java文件)编译为二进制字节码(.class文件),按照JVM规范可知类文件结构(包含魔数、版本、常量池、访问标识与继承信息、成员变量信息、方法信息),可将二进制字节码翻译为JVM指令(这里可借助Oracle的javap工具反编译 class 文件),JVM跨平台的基础便依赖JVM指令,字节码指令最终由解释器逐行解释为机器码交由CPU执行.
1 程序计数器
定义 | 特点 |
---|---|
用来记住下一条jvm指令的执行地址,供解释器循环读取 (物理上使用CPU中寄存器快速读取) |
①是线程私有的 ②不会存在内存溢出(规定) |
2 虚拟机栈
定义 | 垃圾回收 | 内存溢出 | 程序报错 | 线程安全 | 执行参数 |
---|---|---|---|---|---|
栈为方法的内存占用(参数、局部变量、返回地址占用), 虚拟机栈就由多个栈帧组成, 虚拟机栈对应线程,栈帧对应方法,从栈的数据结构来理解, 调用线程内方法会压栈,方法执行完毕会弹栈, 栈顶则为活动栈帧(栈内唯一),对应着当前正在执行的方法 |
不涉及垃圾回收因为方法结束会弹栈 |
存在内存溢出,栈帧过大或过多都可能溢出,因此需要合理分配内存 | java.lang.StackOverflowError 栈内存溢出 原因分析: ①方法递归调用 ②类之间循环引用 |
①方法是否用到非局部变量 ②局部变量是否逃离方法作用范围 |
-Xss1024k 栈内存大小 |
2.1 线程诊断运行(jstack命令)
案例一:
假设问题为cpu占用过高
定位
①用top定位哪个进程对cpu的占用过高
②ps H -eo pid,tid,%cpu | grep 进程id (用ps命令进一步定位是哪个线程引起的cpu占用过高)
H 输出进程信息
-eo pid,tid,%cpu 过滤出进程号,线程号,cpu占用率
③jstack 进程id
④可以根据线程id 找到有问题的线程,进一步定位到问题代码的源码行号
第②步中的tid十进制换为十六进制,对应的就是nid号
案例二:
程序运行很长时间没有结果
定位
通过jstack 可以看到问题为deadlock
3 本地方法栈
java调用本地native方法和操作系统打交道时,为其方法提供的内存
4 堆
定义 | 垃圾回收 | 内存溢出 | 程序报错 | 线程安全 |
---|---|---|---|---|
new 关键字,创建对象都会使用堆内存 | 有垃圾回收机制 | 对象创建过多可能OOM | java.lang.OutOfMemoryError:Java heap space 堆内存溢出 |
线程共享的,堆中对象都需要考虑线程安全的问题 |
4.1 堆内存划分(分代GC)
对象首先分配在伊甸园区域
新生代空间不足时,触发 minor gc,伊甸园和 from 存活的对象使用 copy 复制到 to 中,存活的
对象年龄加 1并且交换 from to
minor gc 会引发 stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行
当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)
当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发 full gc,STW的时间更长
4.2 相关VM参数
含义 | 参数 |
---|---|
堆初始大小 | -Xms |
堆最大大小 | -Xmx 或 -XX:MaxHeapSize=size |
新生代大小 | -Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size ) |
幸存区比例(动态) | -XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy |
幸存区比例 | -XX:SurvivorRatio=ratio |
晋升阈值 | -XX:MaxTenuringThreshold=threshold |
晋升详情 | -XX:+PrintTenuringDistribution |
GC详情 | -XX:+PrintGCDetails -verbose:gc |
FullGC 前 MinorGC | -XX:+ScavengeBeforeFullGC |
4.3堆内存诊断工具
命令 | 作用 |
---|---|
jps | 查看当前系统中有哪些 java 进程 |
jmap -heap 进程号 | 查看堆内存占用情况(堆配置参数+各区域占用情况) |
jconsole | 图形界面的,多功能的监测工具,可以连续监测 |
jvirsualvm | 可视化虚拟机, 堆转储dump, 可查看对象个数和大小 |
5 方法区
定义 | 垃圾回收 | 内存溢出 | 程序报错 | 线程安全 | 执行参数 | 工具 |
---|---|---|---|---|---|---|
存储已被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等 | 无 | 大量的类被加载导致元空间(1.6永久代)内存溢出 | java.lang.OutOfMemoryError:Metaspace 元空间内存溢出 java.lang.OutOfMemoryError: PermGen space 永久代内存溢出 |
线程共享的 | -XX:MaxMetaspaceSize=8m -XX:MaxPermSize=8m |
javap -v 类 反编译查看方法区信息 |
版本 | 存储位置 |
---|---|
1.6 (1.8以前) | 占用堆内存,由永久代实现 |
1.8 | 提出元空间概念,由本地内存管理,不归JVM内存管理,但是串池StringTable存放堆中 |
5.1 常量池(constant pool)
参考:常量池和StringTable
6 直接内存
定义 | 特点 | 内存溢出 |
---|---|---|
由原来的数据经由系统内存缓冲区(java不可访问)拷贝到JVM内存缓冲区供JVM访问的方式 ,改为,数据直接写入direct memory直接内存缓冲区供JVM读取的方式 |
常见于 NIO 操作时,用于数据缓冲区 分配回收成本较高,但读写性能高 不受 JVM 内存回收管理 |
java.lang.OutOfMemoryError:Direct buffer memory 系统内存不足导致直接内存溢出 |
6.1 分配和回收原理
使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法文章来源:https://www.toymoban.com/news/detail-823725.html
ByteBuffer 的实现类内部,使用了 Cleaner (虚引用)来监测 ByteBuffer 对象,一旦ByteBuffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调用 freeMemory 来释放直接内存文章来源地址https://www.toymoban.com/news/detail-823725.html
到了这里,关于JVM内存结构的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!