- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
Java 虚拟机栈:Java Virtual Machine Stacks,每个线程运行时所需要的内存
每个方法被执行时,都会在虚拟机栈中创建一个栈帧 stack frame(一个方法一个栈帧)
Java 虚拟机规范允许 Java 栈的大小是动态的或者是固定不变的
虚拟机栈是每个线程私有的,每个线程只能有一个活动栈帧,对应方法调用到执行完成的整个过程
每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存,每个栈帧中存储着:
局部变量表:存储方法里的 Java 基本数据类型以及对象的引用
动态链接:也叫指向运行时常量池的方法引用
方法返回地址:方法正常退出或者异常退出的定义
操作数栈或表达式栈和其他一些附加信息
设置栈内存大小:-Xss size``-Xss 1024k
虚拟机栈特点:
栈内存不需要进行GC,方法开始执行的时候会进栈,方法调用后自动弹栈,相当于清空了数据
栈内存分配越大越大,可用的线程数越少(内存越大,每个线程拥有的内存越大)
方法内的局部变量是否线程安全:
如果方法内局部变量没有逃离方法的作用访问,它是线程安全的(逃逸分析)
如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全
异常:
局部变量表也被称之为局部变量数组或本地变量表,本质上定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量
局部变量表最基本的存储单元是 slot(变量槽):
栈:可以使用数组或者链表来实现
操作数栈:在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈(push)或出栈(pop)
栈顶缓存技术 ToS(Top-of-Stack Cashing):将栈顶元素全部缓存在 CPU 的寄存器中,以此降低对内存的读/写次数,提升执行的效率
基于栈式架构的虚拟机使用的零地址指令更加紧凑,完成一项操作需要使用很多入栈和出栈指令,所以需要更多的指令分派(instruction dispatch)次数和内存读/写次数,由于操作数是存储在内存中的,因此频繁地执行内存读/写操作必然会影响执行速度,所以需要栈顶缓存技术
动态链接是指向运行时常量池的方法引用,涉及到栈操作已经是类加载完成,这个阶段的解析是动态绑定
[外链图片转存中…(img-40Zfh1tb-1658977927894)]
常量池的作用:提供一些符号和常量,便于指令的识别
[外链图片转存中…(img-yDv5SyPY-1658977927895)]
Return Address:存放调用该方法的 PC 寄存器的值
方法的结束有两种方式:正常执行完成、出现未处理的异常,在方法退出后都返回到该方法被调用的位置
正常完成出口:执行引擎遇到任意一个方法返回的字节码指令(return),会有返回值传递给上层的方法调用者
异常完成出口:方法执行的过程中遇到了异常(Exception),并且这个异常没有在方法内进行处理,本方法的异常表中没有搜素到匹配的异常处理器,导致方法退出
两者区别:通过异常完成出口退出的不会给上层调用者产生任何的返回值
栈帧中还允许携带与 Java 虚拟机实现相关的一些附加信息,例如对程序调试提供支持的信息
本地方法栈是为虚拟机执行本地方法时提供服务的
JNI:Java Native Interface,通过使用 Java 本地接口书写程序,可以确保代码在不同的平台上方便移植
不需要进行 GC,与虚拟机栈类似,也是线程私有的,有 StackOverFlowError 和 OutOfMemoryError 异常
虚拟机栈执行的是 Java 方法,在 HotSpot JVM 中,直接将本地方法栈和虚拟机栈合二为一
本地方法一般是由其他语言编写,并且被编译为基于本机硬件和操作系统的程序
当某个线程调用一个本地方法时,就进入了不再受虚拟机限制的世界,和虚拟机拥有同样的权限
本地方法可以通过本地方法接口来访问虚拟机内部的运行时数据区
直接从本地内存的堆中分配任意数量的内存
可以直接使用本地处理器中的寄存器
图片来源:https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20%E8%99%9A%E6%8B%9F%E6%9C%BA.md
Program Counter Register 程序计数器(寄存器)
作用:内部保存字节码的行号,用于记录正在执行的字节码指令地址(如果正在执行的是本地方法则为空)
原理:
特点:
Java 反编译指令:
javap -v Test.class
#20:代表去 Constant pool 查看该地址的指令
0: getstatic #20 // PrintStream out = System.out;
3: astore_1 // --
4: aload_1 // out.println(1);
5: iconst_1 // --
6: invokevirtual #26 // --
9: aload_1 // out.println(2);
10: iconst_2 // --
11: invokevirtual #26 // --
Heap 堆:是 JVM 内存中最大的一块,由所有线程共享,由垃圾回收器管理的主要区域,堆中对象大部分都需要考虑线程安全的问题
存放哪些资源:
对象实例:类初始化生成的对象,基本数据类型的数组也是对象实例,new 创建对象都使用堆内存
字符串常量池:
字符串常量池原本存放于方法区,JDK7 开始放置于堆中
字符串常量池存储的是 String 对象的直接引用或者对象,是一张 string table
静态变量:静态变量是有 static 修饰的变量,JDK8 时从方法区迁移至堆中
线程分配缓冲区 Thread Local Allocation Buffer:线程私有但不影响堆的共性,可以提升对象分配的效率
设置堆内存指令:-Xmx Size
内存溢出:new 出对象,循环添加字符数据,当堆中没有内存空间可分配给实例,也无法再扩展时,就会抛出 OutOfMemoryError 异常
堆内存诊断工具:(控制台命令)
jhsdb jmap --heap --pid 进程id
在 Java7 中堆内会存在年轻代、老年代和方法区(永久代):
分代原因:不同对象的生命周期不同,70%-99% 的对象都是临时对象,优化 GC 性能
public static void main(String[] args) {
// 返回Java虚拟机中的堆内存总量
long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
// 返回Java虚拟机使用的最大堆内存量
long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
System.out.println("-Xms : " + initialMemory + "M");//-Xms : 245M
System.out.println("-Xmx : " + maxMemory + "M");//-Xmx : 3641M
}
方法区:是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、即时编译器编译后的代码等数据,虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是也叫 Non-Heap(非堆)
方法区是一个 JVM 规范,永久代与元空间都是其一种实现方式
方法区的大小不必是固定的,可以动态扩展,加载的类太多,可能导致永久代内存溢出 (OutOfMemoryError)
方法区的 GC:针对常量池的回收及对类型的卸载,比较难实现
为了避免方法区出现 OOM,在 JDK8 中将堆内的方法区(永久代)移动到了本地内存上,重新开辟了一块空间,叫做元空间,元空间存储类的元信息,静态变量和字符串常量池等放入堆中
类元信息:在类编译期间放入方法区,存放了类的基本信息,包括类的方法、参数、接口以及常量池表
常量池表(Constant Pool Table)是 Class 文件的一部分,存储了类在编译期间生成的字面量、符号引用,JVM 为每个已加载的类维护一个常量池
运行时常量池是方法区的一部分
虚拟机内存:Java 虚拟机在执行的时候会把管理的内存分配成不同的区域,受虚拟机内存大小的参数控制,当大小超过参数设置的大小时就会报 OOM
本地内存:又叫做堆外内存,线程共享的区域,本地内存这块区域是不会受到 JVM 的控制的,不会发生 GC;因此对于整个 Java 的执行效率是提升非常大,但是如果内存的占用超出物理内存的大小,同样也会报 OOM
本地内存概述图:
PermGen 被元空间代替,永久代的类信息、方法、常量池等都移动到元空间区
元空间与永久代区别:元空间不在虚拟机中,使用的本地内存,默认情况下,元空间的大小仅受本地内存限制
方法区内存溢出:
-XX:MaxPermSize=8m #参数设置
-XX:MaxMetaspaceSize=8m #参数设置
元空间内存溢出演示:
public class Demo1_8 extends ClassLoader { // 可以用来加载类的二进制字节码
public static void main(String[] args) {
int j = 0;
try {
Demo1_8 test = new Demo1_8();
for (int i = 0; i < 10000; i++, j++) {
// ClassWriter 作用是生成类的二进制字节码
ClassWriter cw = new ClassWriter(0);
// 版本号, public, 类名, 包名, 父类, 接口
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
// 返回 byte[]
byte[] code = cw.toByteArray();
// 执行了类的加载
test.defineClass("Class" + i, code, 0, code.length); // Class 对象
}
} finally {
System.out.println(j);
}
}
}
直接内存是 Java 堆外、直接向系统申请的内存区间,不是虚拟机运行时数据区的一部分,也不是《Java 虚拟机规范》中定义的内存区域
直接内存详解参考:NET → NIO → 直接内存
变量的位置不取决于它是基本数据类型还是引用数据类型,取决于它的声明位置
静态内部类和其他内部类:
类变量:
实例变量:
局部变量:
类常量池、运行时常量池、字符串常量池有什么关系?有什么区别?
什么是字面量?什么是符号引用?
int a = 1; //这个1便是字面量
String b = "iloveu"; //iloveu便是字面量
system.in.read()
cpu 占用过多
可以根据线程id 找到有问题的线程,进一步定位到问题代码的源码行号
- 演示永久代内存溢出 java.lang.OutOfMemoryError: PermGen space
-XX:MaxPermSize=8m
* 演示元空间内存溢出 java.lang.OutOfMemoryError: Metaspace
* -XX:MaxMetaspaceSize=8m
常量池中的字符串仅是符号,第一次用到时才变为对象
利用串池的机制,来避免重复创建字符串对象
字符串变量拼接的原理是 StringBuilder (1.8)
字符串常量拼接的原理是编译期优化
可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池
1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回
1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池, 会把串池中的对象返回
查看GC机制:jmap -dump:format=b,live,file=文件名.bin 进程ID
被引用计数+1 如果某个变量不再引用它就-1 变为0则可以被回收**缺点:**循环引用
JVM中的垃圾回收期是采用可达性分析算法来探索所有存活的对象:扫描堆中的对象看是否能够沿着GC Root对象作为起点为引用链找到该对象,若找不到则可以回收(本ROOT对象直接或间接引用的对象不可以被回收)
局部变量引用的对象可以作为根对象
方法参数可以作为跟对象
ROOT对象的分类:系统类,本地方法(操作系统的方法),运行的线程,以及锁
强引用,软引用,弱引用,虚引用,终结器引用
当被软引用和弱引用创建的时候分配了引用队列,那么当软弱引用引用的对象被回收时,软弱引用将会进入到该引用队列中,(优点:软弱引用也需要占用一定的内存,如果需要操作软弱引用占的内存,那就需要用引用队列来找到这两个引用)
虚引用与终结器引用创建时必须要关联引用队列来使用
引用队列
//引用队列
RederenceQueue<byte[]> queue = new RederenceQueue<byte[]>;
//关联了引用队列,当软引用所关联的byte[]被回收时,软引用会自己加入到queue中
SoftReference<byte[]> ref = new SoftReference<byte[]>(new byte[1024*4*1024],queue);
Reference<? extends byte[]> poll = queue.poll();
while(poll != null){
list.remove(poll);
poll = queue.poll();
}
优点:垃圾回收的速度快 缺点:空间不连续,会产生内存碎片
三种垃圾回收算法,视情况而定---------------------------------->分代回收
新生代 ---- > 垃圾回收执行的比较频繁 Minor GC
伊甸园
幸存区From
幸存区 To
老年代 — >垃圾回收执行的频率比较低 Full GC
对象首先分配在伊甸园区域
新生代空间不足时,触发minor gc,伊甸园和from存活的对象copy到to中,存活的对象年龄也要加1,并且交换from to
minor gc 会引发 stop the world(STW暂停其他用户线程),等垃圾回收结束,用户线程才恢复运行
当对象寿命超过阈值时,会晋升值老年代,最大寿命是15(4bit)
当老年代空间不足,会先尝试触发minor gc,如果之后空间仍不足,那么触发full gc(STW的时间更长)如果full gc执行后空间还是不足的话就会报:内存溢出 java.lang.OutOfMemoryError: PermGen space
----------------------->分代回收
新生代 ---- > 垃圾回收执行的比较频繁 Minor GC
伊甸园
幸存区From
幸存区 To
老年代 — >垃圾回收执行的频率比较低 Full GC
对象首先分配在伊甸园区域
新生代空间不足时,触发minor gc,伊甸园和from存活的对象copy到to中,存活的对象年龄也要加1,并且交换from to
minor gc 会引发 stop the world(STW暂停其他用户线程),等垃圾回收结束,用户线程才恢复运行
当对象寿命超过阈值时,会晋升值老年代,最大寿命是15(4bit)
当老年代空间不足,会先尝试触发minor gc,如果之后空间仍不足,那么触发full gc(STW的时间更长)如果full gc执行后空间还是不足的话就会报:内存溢出 java.lang.OutOfMemoryError: PermGen space
对于OopMap与安全点和安全区域与RememberedSet 理解请看我的这篇Blog:
OopMap与安全点和安全区域与RememberedSet 理解
是否可以复制一个完整的 JVM,并且在故障转移的情况下只需将负载转移到复制的 JVM 上? 如果是,那我们该怎么做呢? 最佳答案 如果您的应用程序是 Web 应用程序,请阅读“集群”和“负载平衡”。大
我读了下面的话,但我想知道它们之间的区别...... JVM 规范、JVM 实现、JVM 运行时 最佳答案 JVM 规范:描述 JVM 应如何运行的文档。 JVM 实现:基于 JVM 规范的 JVM
我目前有四个不同的 java 应用程序,它们由 .bat 文件启动的 jar 运行,这些文件位于 Windows XP Embedded 开始菜单的 starup 文件夹中。我还启动了 Firefox
有人能给我一些关于强制 64 位 jvm 作为 32 位 jvm 运行的想法吗? 我需要为蓝牙连接编写一个 jse 桌面应用程序。为此,我需要实现 Bluecove jar 。它只有 32 位文件。所
我看到过关于这个问题的多条评论——有人说是,有人说不是,许多答案模棱两可。任何人都可以用更简单的术语描述它所在的位置吗?在一篇文章中,我什至看到有人说它与类加载器加载类的类内存共享相同的内存位置 -
我正在寻找所有可能的 jvm 退出代码的完整列表(不是 java System.exit(x))。我使用搜索引擎唯一能找到的是 SIGTERM 退出代码列表:http://journal.thobe.
为了监视任何正常的 Java 进程 JVM,我们可以使用 Attach API。是否有可用于监控 WebSphere JVM 的 API? 最佳答案 您可以使用 PMI(性能监控基础设施)来监控 JV
这个问题在这里已经有了答案: 8年前关闭。 Possible Duplicate: Java - C-Like Fork? 我想知道如何从 JDK fork 子 JVM,甚至有可能这样做吗? 一些框架
JVM 上的哪些图灵完备语言实现不使用 JVM 堆栈作为调用堆栈? (我问是因为我想在同一个线程中实现 Scala 和另一种语言之间的协程。) 最佳答案 闪蝶 SISC(方案代码的第二解释者) 曾经不
我看到here除了 Java 之外,还有很多语言可以在 JVM 上运行。我对在 JVM 中运行的其他语言的整个概念有些困惑。所以: 为 JVM 使用其他语言有什么优势? 为 JVM 编写语言/编译器需
我已经运行了 straced JVM (OpendJDK 11): strace -e trace=mmap java -Xms8192m Main 输出是: mmap(NULL, 8192, PRO
我已经运行了 straced JVM (OpendJDK 11): strace -e trace=mmap java -Xms8192m Main 输出是: mmap(NULL, 8192, PRO
我编写了一个简单的数独求解器。为了粗略测试性能,我使用简单的 System.currentTimeMillis 调用。 我在文本文件中准备了一组初始数独配置。该程序读取该文件并解决每个数独配置。运行测
JVM 被广泛使用:Scala、Groovy、Jython 等。我听说它被描述为“卓越”、“出色”和“严重低估”。为什么? 更具体地说,是什么让 JVM 独一无二?随着所有资金投入 .NET,或者 C
这个问题在这里已经有了答案: 10年前关闭。 Possible Duplicate: Are there any Java VMs which can save their state to a fi
想象一下 6-7 台服务器的设置都完全相同Java 版本“1.6.0_18”OpenJDK 运行时环境 (IcedTea6 1.8) (fedora-36.b18.fc11-i386)OpenJDK
(如有错误请指正) 我了解到,当您通过发出 java 命令来运行 java 程序时, java MyProg 程序将在新的 JVM 上运行。 什么将程序加载到新的 JVM 中?是生成新线程的 JRE
我们有一个使用 JNI 的桌面应用程序偶尔会导致 JVM 崩溃。幸运的是,JVM 会生成一个 hs_err_pidXXXX.log 文件,这对于调试此类错误非常有用。然而,它似乎总是转到当前工作目录,
我在命令提示符下运行一个程序集 jar 文件并得到下面的异常。并导致终止。 Uncaught error from thread [ccp-akka.persistence.dispatchers.d
一、什么是Java虚拟机 虚拟机:指以软件的方式模拟具有完整硬件系统功能、运行在一个完全隔离环境中的完整计算机系统 ,是物理机的软件实现。常用的虚拟机有VMWare,Visual Box,Java
我是一名优秀的程序员,十分优秀!