JVM的内存结构学习笔记

424 阅读4分钟

1、程序计数器(Program Counter Register)

1.1 作用:是记住下一条jvm指令的执行地址

1.2 特点:

1)线程私有,随着线程创建而创建,销毁而销毁

2)不会存在内存溢出

3)是一块较小的内存空间

2、虚拟机栈(JVM stacks)

2.1 栈与栈帧

栈:线程运行需要的内存空间,一个线程对应一个栈,一个栈由多个栈帧组成,每个线程只能有一个活动栈帧,对应着当前正在运行的方法。

栈帧:每个方法运行时需要的内存,用于装载方法的参数、变量、返回值等信息。

截图1.png

2.2 问题辨析

1)垃圾回收不涉及栈内存,因为栈会自动弹出栈帧释放内存。垃圾回收只会设计堆内存的内容

2)栈内存不是越大越好,当栈内存加大之后,可分配的栈数量会变少从而导致多线程运行的数量变少。栈内存的值只是为了可以多次调用方法。

3)如果方法内的局部变量没有逃离方法的作用范围,那么他是线程安全的

4)如果局部变量引用了对象,并且该对象逃离了方法的作用范围(方法参数,返回值等),则需要考虑线程安全问题

2.3 栈内存溢出

1)栈帧过多导致栈内存溢出 --> StackOverflowError

2)栈帧过大导致----

2.4 线程运行诊断

1)cpu占用过多

用top命令定位哪个进程对cpu使用高

使用ps命令进一步定位是哪个线程引起的

使用jstack找到问题的代码行

2)长时间未有结果

使用jstack查看问题代码行

3、本地方法栈

调用本地的方法接口

4、堆

4.1概念:

通过new关键字创建对象都会使用堆内存

堆中有垃圾回收机制

都需要考虑线程安全问题

4.2

堆内存溢出 OutOfMemoryError : Java heap space

4.3 堆内存诊断

1)jps工具

使用jps工具查看当前有那些Java进程

2)jmap工具

使用命令:

jmap -heap 进程id

可以查看进程的详情信息

3)jconsole工具

使用命令

jconsole打开图形化界面

4)jvisualvm工具

使用命令jvisualvm打开

5、方法区

运行时常量池

.class的二进制字节码中包含了:类的基本信息,常量池,类方法定义,虚拟机指令等

常量池中保存着虚拟机指令中的信息,常量池可认为是一张常量表,当类被加载时,它的常量池信息就会被放入运行时常量池(内存中)

常量池最初是存在字节码文件中,当被运行时就会被加载到内存中,也就是称为运行时常量池

6、StringTable(字符串常量池 )[jdk1.8]

Java代码:

当未执行时,a只是常量池中的符号,还没Java字符串对象

截图2.png

红线:要去常量池的2号位置加载一个信息(把a符号变为"a"字符串对象)

然后把“a”对象视为一个key,去StringTable中找是否有取值相同的Key,没有就放入串池中,如果有就使用串池中的对象

StringTable是 hashTable结构,不能扩容

绿线:存入到1号局部变量中去

截图3.png

LocalVariableTable是方法运行的局部变量表

截图4.png

代码:

0

截图5.png 13行可以看出是使用StringBuilder

第16行是从表中的1号位加载信息

21和24行使用StringBuilder的append方法

整个String s4 = s1 +s2 可以看作是:new StringBuilder().append("a").append("b").toString(),相当于是new String("ab"),放入堆中的,并没有放入串池中

截图6.png 代码:

截图7.png 第6行是String s3 = "ab",第11行是String s4 = "a" + "b"

String s4 = "a" + "b"是直接去字符串常量池中找"ab",所以s4和s3是相同的

这是javac在编译期间的优化,结果已经在编译器确定为ab

而之前的 是不确定的,因为s1和s2是可以更改的

0

代码:

截图8.png

①new String("a") 时,先将"a"放入串池中,再通过取出"a"并在堆中new一个String对象

②s由有两个不确定的字符串拼接而成,所以只会在堆中创建"ab",并不会在串池中放入"ab"

③intern()方法可以将当前字符串对象放入串池中,如果有则不会放入,没有则放入串池,并把串池中的对象地址返回

④如果是在jdk1.6中,intern()方法则会将s拷贝一份,将这份复制后的放入串池中,所以放入串池中的s和原来的s是不同的

在1.6的时候,StringTable在方法区的永久代中,之后的在堆内存中

垃圾回收机制

当串池中的对象太多时,会回收没被使用的字符串从而节省空间

性能调优

StringTableSize的桶大小影响速度

使用串池可以有效地节约内存的使用。当有重复的字符串时可以直接从串池中获取。