内存结构
程序计数器
Program Counter Register 程序计数器(寄存器)
作用:是记住下一条jvm指令的执行地址特点
- 是线程私有的,一个线程一个程序计数器。线程的嵌套调用就依赖于程序计数器。
- 不会存在内存溢出
虚拟机栈
Java Virtual Machine Stacks (Java 虚拟机栈)
方法运行时使用的内存,比如main方法运行,进入方法栈,存放方法中的局部变量
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。栈中可执行的方法数量是有限的,不停的递归调用方法,可能会造成栈内存溢出。但是并不是栈内存越大越好,栈内存大了,相应的能创建的线程就会减少。
方法内的局部变量是否线程安全?
- 如果方法内局部变量没有逃离方法的作用访问,它是线程安全的
- 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全
本地方法栈
调用本地方法(native),为本地方法提供的内存空间
本低方法:非java语言写的代码 不同操作系统,本地方法不同
例: clone() hashcode() notify()
Heap 堆
通过 new 关键字,创建对象都会使用堆内存,成员变量都在堆内存
jdk7之后,字符串常量池也在堆内存中
特点
- 它是线程共享的,堆中对象都需要考虑线程安全的问题
- 有垃圾回收机制
常用诊断工具
- jps 工具
查看当前系统中有哪些 java 进程 - jmap 工具
查看堆内存占用情况 jmap - heap 进程id - jconsole 工具
图形界面的,多功能的监测工具,可以连续监测 - visualvm工具(jdk8以后需要自己下载)
visualvm下载安装
方法区
储存可以运行的class文件,包含方法的信息,静态区,成员方法
内存结构
jdk1.6 永久代
jdk1.8元空间
常量池
- (静态)常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量
等信息 - 运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址
字符串常量池
底层是hash表
- 常量池中的字符串仅仅是符号,第一次用到才会变为对象
- 字符串变量拼接
- 字符串常量拼接,编译期优化
- 可以使用intern方法,将串池中还没有的数据放入到串池。
- jdk1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回
- 1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池, 会把串池中的对象返回
从字节码的角度分析,jdk9
@Test
public void test01(){
String s1 = "a";
String s2 = "b";
String s5 = "ab";
String s3 = s1+s2;
String s4 = "a"+"b";
}
反编译命令javap -v 文件名.class