一、程序计数器
作用:是记住下一条jvm指令的执行地址
特点:
是线程私有的
不会存在內存溢出
二、虚拟机栈
每个线程运行时所需要的内存,称为虚拟机栈
每个栈由多个栈帧(Frame) 组成,对应着每次方法调用时所占用的内存
每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法
问题辨析
1.垃圾回收是否涉及栈内存? 不会,垃圾回收是回收堆内存中的无用对象,栈内存中是一次次的方法调用产生的栈帧內存,而在每次方法调用完成之后,都会被弹出栈,自动被回收。
2.栈内存分配越大越好吗? 不是。栈內存可以在运行代码时,通过虚拟机参数进行指定(-Xss1m),物理內存的大小一定,栈內存越大,划分的线程数越小
3.方法内的局部变量是否线程安全?
■ 如果方法内局部变量没有逃离方法的作用访问,它是线程安全的
■ 如果是局部变量引用了对象,并逃离方法的作用方法,需要考虑线程安全
栈內存溢出(java.lang.*Error)
栈帧过多导致栈內存溢出(方法的递归调用,没有设置一个正确的结束条件)
栈帧过大导致栈內存溢出(一般不会出现)
线程运行诊断
案例 1: cpu 占用过多
定位
■用top定位哪个进程对cpu的占用过高
■psH -eo pid,id,%cpu | grep进程id (用ps命令进- 步定位是哪个线程引|起的cpu占用过高)
■jstack 进程id
■可以根据线程id找到有问题的线程,进一步定位到问题代码的源码行号
案例 2:程序运行很长时间没有结果
程序内部可能发生死锁
三、本地方法栈
作用: java虚拟机在调用本地方法(C/C++ 编写的本地方法)时,需要给本地方法提供一些內存空间
四、堆
定义 : 通过new关键字,创建对象都会使用堆內存
特点:它是线程共享的,堆中对象都要考虑线程安全的问题,有垃圾回收机制
堆内存溢出(java.lang.OutOfMemoryError):堆中对象过多,且都是有用对象,导致堆内存溢出 , 程序运行过程中也可以设置最大堆空间(-Xmx8m)
堆內存诊断
1.jps工具
查看当前系统中有哪些java进程
2. jmap工具
查看堆内存占用情况 jmap -heap 进程id
3. jconsole工具
图形界面的,多功能的监测工具,可以连续监测
4.jvirsualvm工具
图形界面的,功能更加齐全,可定位到具体的对象的属性
五、方法区
官方定义:Java虚拟机中有一个被所有jvm线程共享的方法区。方法区有点类似于传统编程语言中的编译代码块或者操作系统层面的代码段。它存储着每个类的构造信息,譬如运行时的常量池,字段,方法数据,以及方法和构造方法的代码,包括一些在类和实例初始化和接口初始化时候使用的特殊方法。方法区在jvm启动时候被创建。虽然方法区在逻辑层面上是堆的一部分,但是就简单实现来说既不会被回收也不会被压缩。这个规范并不强制指定方法区存放的位置也不会对编译过的代码有管理策略的限制。方法区可能有一个固定的大小或者也可以通过计算大小去扩展也可以在不需要的时候被压缩。方法区的内存也不需要是连续的。Jvm虚拟机实现可以提供给编程人员或者用户初始化方法区的大小,同时在方法区可变大小的情况下,控制这个方法区的最大值和最小值。
程序启动时可手动设置方法区大小(jdk1.8 -XX:MaxMetaspaceSize=8m 、jdk1.6 -XX:MaxPermSize=8m)
1.6版本
1.8版本
内存溢出:当类加载器加载大数量的类到內存中,可能会出现內存溢出(前提:设置了方法区的最大元空间大小),1.8以前是导致永久代的內存溢出,1.8以后是导致元空间的內存溢出
常量池:就是一张表,存在于字节码文件里,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
运行时常量池:常量池是* .class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址
StringTable的特性:
常量池中的字符串仅是符号,第一次用到时才变为对象
利用串池的机制,来避免重复创建字符串对象
字符串变量拼接的原理是StringBuilder (1.8)
字符串常量拼接的原理是编译期优化
可以使用intern方法,主动将串池中还没有的字符串对象放入串池,并把池串中的对象返回
1.8 将这个字符串尝试放入串池,如果有则不放入,如果没有则放入串池,会把串池中的对象返回
1.6 将这个字符串尝试放入串池,如果有则不放入,如果没有则会把此对象复制一份放入串池,会把串池中的对象返回
StringTable性能调优:
调整-XX:StringTableSize=桶个数
考虑将字符串对象是否入池
六、直接內存
定义:不是java虚拟机的內存,而是操作系统的內存
常见于NIO操作时,用于数据缓冲区
分配回收成本较高,但读写性能高
不受JVM内存回收管理
分配和回收原则:
使用了Unsafe对象完成直接内存的分配回收,并且回收需要主动调用freeMemory方法
ByteBuffer的实现类内部,使用了Cleaner (虚引用) 来监测ByteBuffer对象,一 旦ByteBuffer对象被垃
圾回收,那么就会由ReferenceHandler线程通过Cleaner 的clean方法调用freeMemory来释放直接内存