JVM(二) --- JVM的内存结构
写在文章前:本系列博客是学习黑马程序员JVM完整教程所做笔记,若有错误希望大佬们评论修正
一.概述
JVM的内存结构包括程序计数器(PC Register),虚拟机栈(JVM Stacks),堆内存(heap),方法区(Method Area),本地方法区(Native Method Stacks)
二.程序计数器
- 定义:Program Counter Register 程序计数器(寄存器)
- 作用:记录下一条JVM指令的内存地址
- 特点:① 线程私有的,每个线程都有自己的程序计数器 ②不会存在内存溢出
三.虚拟机栈
定义:Java Virtual Machine Stacks (Java 虚拟机栈)
- 每个线程运行时所需的内存,称为虚拟机栈
- 每个栈由多个栈帧组成,一个栈帧对应一个方法调用。
- 栈帧主要记录方法参数,局部变量,返回地址等。
- 每个栈只有一个活动栈帧,对应着正在执行的那个方法
public class Demo1_1 {
public static void main(String[] args) throws InterruptedException {
method1();
} private static void method1() {
method2(1, 2);
} private static int method2(int a, int b) {
int c = a + b;
return c;
}
}
问题辨析:
- 如果方法内局部变量没有逃离方法的作用范围,则线程安全
- 如果局部变量引用了对象,并逃离了作用范围,则需要考虑线程安全
栈内存溢出问题:
java.lang.StackOverflowError
- 栈帧过多导致栈内存溢出(例如方法的递归调用而没有终结点),
-Xss256k 设置JVM栈内存参数
- 栈帧过大导致栈内存溢出(不常见)
线程运行时诊断:
- 用top定位哪个进程对cpu的占用过高
- ps H -eo pid,tid,%cpu | grep 进程id (用ps命令进一步定位是哪个线程引起的cpu占用过高)
- jstack 进程id 可以根据线程id 找到有问题的线程,进一步定位到问题代码的源码行号

四.本地方法栈
- 本地方法 -- 指那些不是用java编写的方法。JVM会为它们申请一些内存
五.堆
定义:heap 堆
- 通过new关键字,创建对象都会使用堆内存
特点:
- 堆内存是线程共享的,堆中对象需要考虑线程安全问题
- 有垃圾回收机制
堆内存溢出:
java.lang.OutOfMemoryError: Java heap space
-Xmx8m 设置JVM堆内存大小
堆内存诊断:
jps 工具 查看当前系统中有哪些 java 进程
jmap 工具 查看堆内存占用情况 jmap - heap 进程id (不能连续监测)
jconsole 工具 图形界面的,多功能的监测工具,可以连续监测
jvisualvm工具
六.方法区
The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the "text" segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods used in class and instance initialization and interface initialization.
The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. This specification does not mandate the location of the method area or the policies used to manage compiled code. The method area may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger method area becomes unnecessary. The memory for the method area does not need to be contiguous.
A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of the method area, as well as, in the case of a varying-size method area, control over the maximum and minimum method area size.
The following exceptional condition is associated with the method area:
If memory in the method area cannot be made available to satisfy an allocation request, the Java Virtual Machine throws an
OutOfMemoryError
.
组成:
方法区内存溢出:
- 1.8 以前会导致永久代内存溢出
* 演示永久代内存溢出 java.lang.OutOfMemoryError: PermGen space
* -XX:MaxPermSize=8m
- 1.8 之后会导致元空间内存溢出
* 演示元空间内存溢出 java.lang.OutOfMemoryError: Metaspace
* -XX:MaxMetaspaceSize=8m
二进制字节码的的组成:
类的基本信息、常量池、类的方法定义(包含了虚拟机的指令)
通过反编译查看字节码文件:
类的基本信息:
常量池:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("hello world");
}
}
常量池:
- 就是一张表如上图的(constant pool),虚拟机指令根据这张常量表找到要执行的类名,方法名,参数类型,字面量信息
运行时常量池:
- 常量池是在.class文件中,当这个类被加载以后,它的常量池信息就会被放入运行时的常量池,并把里面的符号地址变为真实地址
常量池与StringTable的关系:
StringTable串池
特性:
- 常量池中的字符串仅是符号,只有在被用到时才会转化为对象
- 利用串池的机制避免重复创建字符串对象
- 字符串变量拼接的原理是StringBuilder
- 字符串常量拼接的原理是编译器优化
- 可以使用intern(),主动将串池中没有的字符串对象放入串池 。1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回
// StringTable [ "a", "b" ,"ab" ] hashtable 结构,不能扩容
public class Demo1_22 {
// 常量池中的信息,都会被加载到运行时常量池中, 这时 a b ab 都是常量池中的符号,还没有变为 java 字符串对象
// ldc #2 会把 a 符号变为 "a" 字符串对象
// ldc #3 会把 b 符号变为 "b" 字符串对象
// ldc #4 会把 ab 符号变为 "ab" 字符串对象 public static void main(String[] args) {
String s1 = "a"; // 懒惰的
String s2 = "b";
String s3 = "ab";
String s4 = s1 + s2; // new StringBuilder().append("a").append("b").toString() new String("ab")
String s5 = "a" + "b"; // javac 在编译期间的优化,结果已经在编译期确定为ab System.out.println(s3 == s5); }
}
StringTable垃圾回收
StringTable在内存紧张时会发生内存回收
StringTable调优
- 因为StringTable是由HashTable实现的,所以可以适当增加HashTable中桶的个数,来减少字符串放入串池的事件 (-XX:StringTableSize=xxxx)
- 考虑是否需要字符串对象入池 (可以通过intern方法减少重复入池)
七.直接内存
Direct Memory
- 常见于NIO操作,用于数据缓冲区
- 分配回收成本高,但读写性能强
- 不受JVM内存回收管理
文件读写流程
使用了ByteBuffer
直接内存是java程序和操作系统都可以访问的一块区域,无需再将数据从系统内存复制到java的堆内存,从而提高了效率
释放原理
直接内存的回收不是JVM的垃圾回收来释放的,而是通过unsafe.freeMemory来手动释放
通过
//通过ByteBuffer申请1M的直接内存
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1M);
申请直接内存,但JVM并不能直接回收直接内存中的内容,它是如何实现回收的呢?
查看allocateDirect的实现
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
DirectByteBuffer类
DirectByteBuffer(int cap) { // package-private super(-1, 0, cap, cap);
boolean pa = VM.isDirectMemoryPageAligned();
int ps = Bits.pageSize();
long size = Math.max(1L, (long)cap + (pa ? ps : 0));
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)); //通过虚引用,来实现直接内存的释放,this为虚引用的实际对象
att = null;
}
这里调用了Cleaner的create方法,且后台线程还会对虚引用的对象进行监测。如果虚引用的实际对象(这里是ByteBuffer)被回收后,就会调用Cleaner的clean方法,来清除直接内存中占有的内存
public void clean() {
if (remove(this)) {
try {
this.thunk.run(); //调用run方法
} catch (final Throwable var2) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
if (System.err != null) {
(new Error("Cleaner terminated abnormally", var2)).printStackTrace();
} System.exit(1);
return null;
}
});
}
对应对象的run方法
public void run() {
if (address == 0) {
// Paranoia
return;
}
unsafe.freeMemory(address); //释放直接内存中占用的内存
address = 0;
Bits.unreserveMemory(size, capacity);
}
分配和回收原理
- 使用Unsafe类来完成直接内存的分配回收,回收需要主动调用freeMemory方法
- ByteBuffer内部使用了Cleaner(虚引用)来监测ByteBuffer。一旦ByteBuffer被垃圾回收,那么会由ReferenceHandler线程来调用Cleaner的clean方法调用freeMemory来释放内存
JVM(二) --- JVM的内存结构的更多相关文章
- JVM学习01:内存结构
JVM学习01:内存结构 写在前面:本系列分享主要参考资料是 周志明老师的<深入理解Java虚拟机>第二版. 内存结构知识要点Xmind梳理 案例分析 分析1 package com.h ...
- Oracle数据库基础入门《二》Oracle内存结构
Oracle数据库基础入门<二>Oracle内存结构 Oracle 的内存由系统全局区(System Global Area,简称 SGA)和程序全局区(Program Global Ar ...
- jvm比较详尽的内存结构
JVM内存结构 2012-09-17 15:27:59 分类: Java 本文转自:http://www.blogjava.net/nkjava/archive/2012/03/14/371831. ...
- JVM(一)内存结构
今日开篇 什么是JVM 定义 Java Virtual Machine,JAVA程序的运行环境(JAVA二进制字节码的运行环境) 好处 一次编写,到处运行 自动内存管理,垃圾回收机制 数组下标越界检查 ...
- JVM体系结构-----深入理解内存结构
一.概述 内存在计算机中占据着至关重要的地位,任何运行时的程序或者数据都需要依靠内存作为存储介质,否则程序将无法正常运行.与C和C++相比,使用Java语言编写的程序并不需要显示的为每一个对象编写对应 ...
- [二]Java虚拟机 jvm内存结构 运行时数据内存 class文件与jvm内存结构的映射 jvm数据类型 虚拟机栈 方法区 堆 含义
前言简介 class文件是源代码经过编译后的一种平台中立的格式 里面包含了虚拟机运行所需要的所有信息,相当于 JVM的机器语言 JVM全称是Java Virtual Machine ,既然是虚拟机, ...
- JAVA常见错误处理方法 和 JVM内存结构
OutOfMemoryError在开发过程中是司空见惯的,遇到这个错误,新手程序员都知道从两个方面入手来解决:一是排查程序是否有BUG导致内存泄漏:二是调整JVM启动参数增大内存.OutOfMemor ...
- Java中的OutOfMemoryError的各种情况及解决和JVM内存结构
在JVM中内存一共有3种:Heap(堆内存),Non-Heap(非堆内存) [3]和Native(本地内存). [1] 堆内存是运行时分配所有类实例和数组的一块内存区域.非堆内存包含方法区和JVM内部 ...
- 转:JAVA常见错误处理方法 和 JVM内存结构
OutOfMemoryError在开发过程中是司空见惯的,遇到这个错误,新手程序员都知道从两个方面入手来解决:一是排查程序是否有BUG导致内存泄漏:二是调整JVM启动参数增大内存.OutOfMemor ...
- jvm系列二内存结构
二.内存结构 整体架构 1.程序计数器 作用 用于保存JVM中下一条所要执行的指令的地址 特点 线程私有 CPU会为每个线程分配时间片,当当前线程的时间片使用完以后,CPU就会去执行另一个线程中的代码 ...
随机推荐
- Cert Manager 申请SSL证书流程及相关概念-三
中英文对照表 英文 英文 - K8S CRD 中文 备注 certificates Certificate 证书 certificates.cert-manager.io/v1 certificate ...
- Java 进阶P-4.2+P-4.3
继承 什么是继承:通俗易懂就好像是你继承你了爸的财产,其中你是子类,你爸是父类继承在Java中被称为面向对象的三大的特征,其中他表示的是,从已有的类中派生出新的类,新的类拥有了父类中属性和方法(pri ...
- centos7笔记本使用iptables服务,将笔记本模拟成为出口路由器 PPPOE拨号+NAT+端口映射
郑州洪水,闲置在家,捣鼓捣鼓 centos7笔记本使用iptables服务,将笔记本模拟成为出口路由器 PPPOE拨号+NAT+端口映射 环境: 1.笔记本单网口,无法做路由网关,手里有个闲置的USB ...
- immutable.js学习笔记(六)----- OrderedSet
一.OrderedSet 二.普通Set 与 OrderedSet 注意:普通Set并不是严格的一定是升序的 三.takeWhile 四.升序 sort valueA - valueB 五.降序 va ...
- Solon2 开发之容器,八、动态代理的本质
在 Java 里动态代理,主要分:接口动态代理 和 类动态代理.因为它的代理类都是动态创建的,所以名字里会带上"动态". 官网的有些地方叫"代理",也有些地方叫 ...
- 精美的web前端源码的特效
1.JS自定义烟花特效 这是一款基于JS和Canvas的自定义烟花特效,初始化界面的时候特效是不带声效的绽放,当你点击顶部中间的播放,即可以看到美丽的烟火也可以听到烟花绽放的声音,让你脑海浮现过年团圆 ...
- 6、Collections工具类
1.Collections工具类介绍 Collections 是一个操作 Set.List 和 Map 等集合的工具类 Collections 中提供了一系列静态的方法对集合元素进行排序.查询和修改等 ...
- CMAKE添加库
1.因为pthread不是C标准库,需要链接 2.在CMakeLists.txt里添加: FIND_PACKAGE(Threads REQUIRED) TARGET_LINK_LIBRARIES($ ...
- UBUNTU16.04卸载安装protobuf
1.卸载 sudo apt-get remove libprotobuf-dev which protoc 然后删除路径即可 2.安装 sudo apt-get install autoconf au ...
- Hugging Face 每周速递: 扩散模型课程完成中文翻译,有个据说可以教 ChatGPT 看图的模型开源了
每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...