可以用这张图来描述:
(编译过程将源代码转换为字节码,而字节码并不能直接在CPU上执行,只有机器码才能够。解释器是逐行解析代码,因此执行效率较低。而即时编译器(JIT)则通过将一些频繁执行的“热点代码”一次性编译成机器码,从而避免了重复编译相同代码的开销。这样,在遇到相同的热点代码时,JIT编译器可以直接使用已编译的机器码,显著提高执行效率。)
就是保存当前执行指令的位置,执行完当前指令自动更新。其作用:
线程切换时保持当前状态,以备后续执行
控制分支执行,在程序出现分支时,跳转到不同的代码,确保逻辑的正常执行
异常处理和方法调用
其特点:
其数据结构是先进后出,在JVM中的作用就是保存一个线程运行需要的内存空,栈中保存数据单元又被称做栈帧,每个栈帧就是一个方法运行时需要的内存。
问题辨析:
垃圾回收是否涉及到栈内存?
不会涉及到,它是由栈内存的生命周期和方法调用决定的
栈内存分配越大越好吗?
不一定,电脑的物理内存是一定,栈内存越大,相对应的线程就变少了,执行效率可能比之前都差
方法内的局部变量是否线程安全?
如果存储的东西都是栈私有的就不涉及共享变量,是线程安全的
什么时候会出现栈内存溢出“
案例一:cpu占用过多
使用 top
命令确定哪个进程对CPU的占用较高
使用以下命令进一步查看哪个线程引起了CPU占用过高:
ps H -eo pid,tid,%cpu | grep 线程id
使用 jstack
进一步查看线程的详细堆栈信息:
jstack 线程id
案例二:线程执行很久没结果,一般是死锁
查看步骤和案例一一样,查询对应的进程,在看对应线程,最后看线程栈堆的信息
存储本地方法(例如C或C++编写的代码)调用的栈帧
方法区是JVM中用来存放类级别信息的内存区域,它在JVM内存模型中扮演着重要角色,特别是在类加载、常量存储、静态变量管理等方面。随着JVM的版本更新,方法区的实现从永久代(PermGen)转变为元空间(Metaspace),优化了内存管理和性能。
如图所示:JVM1.6是利用永久代的方法创建方法区,其包括:常量池(StringTable放在常量池)、类文件、类加载文件等,到了1.8就采用元空间的方式,其把常量池、类文件、类加载文件放在本地内存中,由JVM提供一个索引指向这个内存地址,StringTable放在JVM的堆中
场景:
动态代理生成代理类其中的数据就会保存到方法区中,代理类产生太多,且没有及时的清理,这样就会导致内存溢出。
**方法区存储信息:**二进制字节码包括(类的基本信息、常量池、类方法的定义,包括了虚拟机的指令)
**常量池:**就是一张表,虚拟机指令根据这张表,找到要执行的类名、方法名、参数类型、字符信息。常量池找不到才会创建新的常量
运行时常量池:
常量池是 *.class 文件中的,当该类被加载以后,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址
public class StringTableDemo1 {
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "ab";
}
}
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."":()V 【方法引用 #2.#3 表示父类构造方法引用】
#2 = Class #4 // java/lang/Object 【父类的名称】
#3 = NameAndType #5:#6 // "":()V 【该类的构造方法引用, ()V方法签名】
#4 = Utf8 java/lang/Object 【父类的全限定名】
#5 = Utf8
#6 = Utf8 ()V
#7 = String #8 // a 【字符串常量引用】
#8 = Utf8 a 【字符串常量】
#9 = String #10 // b
#10 = Utf8 b
#11 = String #12 // ab
#12 = Utf8 ab
#13 = Class #14 // com/wangxin/StringTableDemo1 【类名】
#14 = Utf8 com/wangxin/StringTableDemo1 【该类的全限定名】
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 LocalVariableTable
#18 = Utf8 this 【局部变量当前引用】
#19 = Utf8 Lcom/wangxin/StringTableDemo1;【描述this是谁应用,即StringTableDemo1 类的引用】
#20 = Utf8 main 【main方法】
#21 = Utf8 ([Ljava/lang/String;)V 【描述main方法为 参数定义为String类型,返回为void】
#22 = Utf8 args
#23 = Utf8 [Ljava/lang/String; 【指定arg参数类型为String】
#24 = Utf8 s1
#25 = Utf8 Ljava/lang/String;
#26 = Utf8 s2
#27 = Utf8 s3
#28 = Utf8 SourceFile
#29 = Utf8 StringTableDemo1.java
主要是存储在元数据中(JDK8),有一些保存在常量池中
{
【构造方法分描述】
public com.wangxin.StringTableDemo1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/wangxin/StringTableDemo1;
【main方法的描述】
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=4, args_size=1
0: ldc #7 // String a
2: astore_1
3: ldc #9 // String b
5: astore_2
6: ldc #11 // String ab
8: astore_3
9: return
LineNumberTable:
【line 12,其中12表示对源代码第几行】
line 12: 0
line 13: 3
line 14: 6
line 15: 9
【局部变量】
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 args [Ljava/lang/String;
3 7 1 s1 Ljava/lang/String;
6 4 2 s2 Ljava/lang/String;
9 1 3 s3 Ljava/lang/String;
}
就是在操作系统缓冲区,开辟一个区域可以有JVM直接方法。目的就是加快文件的传输JVM访问本地数据有两个缓冲区,操作系统和JVM中,经过两个缓冲区就会到传输相对比较慢
缺点