1.什么是JVM?
定义:Java Virtual Machine-java 程序的运行环境(二进制字节码的运行环境)
优点:1.一次编码,到处运行。
2.自动内存管理,垃圾回收功能。
3.数组下标越界越界检查
4.多态
比较:
JVM学习(学习中,同步更新)作用:1.面试2.理解底层的实现原理(长远发展必过)3.中高级程序员必备技能。
学习路线
JVM学习(学习中,同步更新)一.内存结构
1.程序计数器(寄存器)
Program Counter Register
运行过程:
二进制编码—>解释器—>机器码—>CPU
作用:记住下一条jvm指令的执行地址,如果没有它将不知道执行哪条指令
特点:1.是线程私有的2.不会存在内存溢出
二.虚拟机栈(线程运行时需要的内存空间)
JVM学习(学习中,同步更新)栈帧:一个栈帧对应一个方法的调用,是每个方法运行时需要的内存
注意:每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。
问题辨析:
1.垃圾回收是否涉及栈内存?
答:不涉及。
2.栈内存分配越大越好么?
答:栈内存越大,可运行的线程数越少,所以并不是越大越好。
3.方法内的局部变量是否线程安全?
答:局部变量是线程私有的,不会受到其它线程的干扰。
扩展:若为 static int x=0.多线程共享这个数,不加操作就会产生混乱,要考虑线程安全性。
总结:线程是否安全,要看变量是否共享,是否逃离了方法的作用范围(例:有return返回值的方法)
栈内存溢出:
1.栈帧过多导致的(一般)
2.栈帧过大导致
运行错误:java.lang.StackOverflowError.
线程运行诊断:
案例1:cpu占用过多
解决方式:定位到占用线程过高的那个线程,结束进程。
案例2:程序运行长时间没结束
解决方式:造成原因是两个线程产生了死锁,释放线程的锁就解决了。
3.本地方法栈:给本地方法提供一个运行的空间
三.堆
定义:通过关键字new创建的对象都会使用堆内存
特点:
1.它是线程共享的,堆中对象都需要考虑线程安全的问题。
2.有垃圾回收机制。
堆内存的溢出:
报错代码:java.lang.OutofMemoryError:Java heap space
对堆空间设置参数:-Xmx_m(_上填要设置的值)
注:堆空间可以设置小一点,这样可以尽快解决关于堆内存产生的问题。
堆内存诊断:
1.jps工具
查看当前系统中有哪些java进程
2.jmap工具
查看堆内存占用情况。heap 进程id
3.Jconsole工具
图形界面的,多功能的检测工具,可以连续监测。
案例:垃圾回收后内存任然占用很高
分析:对象一直被引用,未能回收。
四.方法区
定义:是所有java虚拟机线程共享的区,它存储了与类相关的一些信息(成员变量、方法数据、成员方法、构造器的方法、类的构造器)
注解:方法区是在虚拟机启动时被创建,逻辑上是堆的一个组成部分,但要注意的是不同厂商制作的都有些许差异。
方法区图解:
JVM内存结构1.6图解
JVM学习(学习中,同步更新)由图可知:在1.6中,方法区的实现叫永久代,String Table叫字符串表(俗称:串池)
JVM内存结构1.8图解
JVM学习(学习中,同步更新)对比:在1.8中,方法区的实现变成了MetaSpace,将不再占用堆内存了,换句话说它已经不是由JVM管理它的内存结构了,它已经被移动到本地内存(操作系统内存)当中,同时StringTable被移到了Heap中。
方法区内存溢出
类加载器的作用:加载类的二进制字节码。
1. 内存结构1.8以前会导致永久代内存溢出
永久代内存溢出报错:
Java.lang.OutofMemoryError:PermGen space
2. 内存结构1.8之后会导致元空间内存溢出
元空间内存溢出报错:
Java.lang.OutofMemoryError:Metaspace
实际场景内存溢出辨析:
当一些项目用到Spring框架和mybatis框架时,经常会产生大量的在运行期间生成的类。(在1.8以前常出现)
常量池作用:给指令提供一些常量符号,根据常量符号去找到对应的类名、方法名、参数类型、字面量等信息。
补充:二进制字节码(类的基本信息、常量池、类方法定义、包含了虚拟机指令)
运行时常量池,常量池是*.Class文件中的,当该类被加载时它的常量池信息就会放入运行时的常量,并把里面的符号地址变为真实地址。
五.StringTable特性
1.常量池中的字符串仅是符号,第一次用到时才变为对象
2.利用串池的机制,来避免重复创建字符串对象
3.字符串常量拼接的原理是编译期优化
注:拼接的仅存在于堆中,串池中没有
4.字符串常量拼接的原理StringBuilder(1.8)
5.可以使用intern主动将串池中还没有的字符串对象放入串池
例:
String s=newString("a")+newString("b");
//new String("ab")
String s2=s.intern();
//将这个字符串对象尝试放入串池,如果有则不会放入,如果没有则放入串池,会把串池中的对象返回。
System.out.println(s2=="ab");
//运行结果为true,因为“ab”这个对象在之前已经被放入串池中了,因此不会创建新的对象,所以s2的对象就是“ab”
System.out.println(s=="ab");
//运行结果为true,因为intern将s的对象“ab”放入了串池中,并把“ab”返回给了s,所以s的对象就是“ab”
网友评论