JVM
-
jvm的位置
-
jvm的体系结构
-
类加载器
1 虚拟机自带的类加载器
2 启动类 根加载器 :引导类加载器,Bootstrap ClassLoader 使用c/c++实现,用来加载java核心库
3 扩展类加载器:Extension ClassLoader 使用java语言编写,父类为启动类加载器,从java.ext.dirs 系统属性所指定的目录中加载类库,从jdk安装目录jre/lib/ext 子目录下加载类库
4 应用程序加载器:系统加载器,AppClassLoader 使用java 语言编写,负责加载环境变量classpath或java.class.path 指定路径下的类库。默认的类加载器,一般java引用的类都是由他来完成加载。
-
双亲委派机制
为了安全,在我们进行类加载的时候,会一层一层的往上委托,在上层父类包中找不到,才一层一层往下找。
-
沙箱安全机制
就是一种保护机制,防止外来的程序损害电脑。
基本组件:
-
字节码校验器:确保java 类文件遵循java语言规范。
-
类装载器:类装载器在 防止恶意代码去干涉善意代码,守护了被信任的类库边界,将代码归入保护域,确定代码可有那些操作...这三个方面起作用
装载器采用的机制是双亲委派模式。
-
存取控制器:用户可以指定存取控制器控制核心api对操作系统的存取权限。
-
安全管理器:实现权限控制,是核心api和操作系统之间的主要接口。
-
安全软件包:允许用户为i自己的应用增加新的安全特性
-
-
Native
凡是带了native 关键字的,说明java的作用范围达不到了,会去调用底层c语言的库
会进入 本地方法栈 调用 本地方法接口JNI 为了扩展java的使用,融合不同的语言
它在内存区专门开辟了一块标记区域:Native Method stack 用来登记native方法
java 程序驱动打印机,管理系统。
-
PC寄存器
Program Counter Register 程序计数器
每个线程都有一个程序计数器,是线程私有的,就是一个指针,指针方法区中的方法字节码(用来存放指向像一条指令的地址,即将要执行的代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。
-
方法区
Method Area
方法区是被所有线程共享的,所有字段的方法字节码,以及一些特殊的方法,如构造函数,接口代码也在此定义。简单来说,所有定义的方法的信息都保存在该区域,此区域属于共享区间
静态变量,常量,类信息(构造方法,接口定义),运行时的常量池存放在方法区中,实例变量存放在堆内存中,和方法区无关。
static final Class 常量池。
-
栈
程序 = 数据结构+算法
队列:先进先出 FIFO
栈:就像一个桶 先进后出,后进先出 数据结构原文中是 模拟一个手枪弹夹,先进的后出。
为什么main()方法先执行,最后结束。
栈内存主管程序的允许,生命周期和线程同步,线程结束,栈内存也就是释放,对于栈来说,不存在垃圾回收问题,一旦线程结束,栈就over。
栈中存放 8 大数据类+引用对象+实例的方法。
栈+ 堆+ 方法区 的交互
-
三种JVM
-
sun公司 HotSpot 我们学习的
-
BEA JRockit
-
IBM J9 VM
-
-
堆
Heap 一个jvm只有一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后,一般把类,方法,常量,变量...保存我们所有引用类型的真实对象:
堆内存中还细分为三个区域:
-
新生区
一个类 诞生和成长的地方,也可能是死亡的地方。
伊甸园,所有对象都是在伊甸园区new出来的
幸存者区(0,1)
在伊甸园区如果这个对象没有被引用了,就会触发轻GC,存活下来的类就存放在幸存区中。
-
养老区
当幸存区里也满了 就会触发重GC,将伊甸园和幸存区进行清理,存活下来的才保存到养老区。
养老区也满了,就会报oom
-
永久区
这个区域常驻内存,用来存放JDK自身携带的Class对象,Interface元数据,存储的是java运行时的一些环境,或者类信息。不存在垃圾回收。
关闭虚拟机才会释放这个区域的内存
只有一个启动类加载了大量的第三方jar包,Tomcat部署了太多的应用,大量动态生成的反射类,不断的被加载,直到内存满了,就会出现OOM;
jdk1.6 :永久代 常量池在方法区
jdk1.7 :永久代 慢慢退化了,去永久代 常量池在堆中
jdk1.8 : 无永久代,常量池在元空间
GC 垃圾回收 主要是在伊甸园区和养老区
堆内存满了,就会报OOM
在jdk8以后,永久存储区叫 元空间
持久代也叫 “非堆”
public class Test004 { public static void main(String[] args) { long l = Runtime.getRuntime().maxMemory();//返回虚拟机试图使用的最大内存 long l1 = Runtime.getRuntime().totalMemory(); //返回虚拟机试图使用的最大内存 System.out.println(l/(double)1024/1024 + "mb"); //jvm 总内存 System.out.println(l1/(double)1024/1024 + "mb"); } }
指定内存大小
OOM:
1 常识扩大堆内存看结果
2 分析内存,看一下那个地方出现了问题(专业工具)
元空间:逻辑上存在,物理上不存在
-
-
堆内存调优
使用JPROFILER软件进行 对堆内存的一个调优。
-
GC,
1 每次gc都会把活得对象移到幸存区,一旦伊甸园区被GC,就会空。
2 为了保证幸存区to 为空 存区to和存区from会交换位置,并将对象都保存到一个幸存区中,那么剩下那个空的就会变为幸存区to。
3 当一个对象经历了15此GC(可以设置次数:-XX:MaxTenuringThreshold=5),都还没有被回收,就会被放入老年区。
好处:没有内存的碎片
坏处:浪费了内存空间,to空间一直是空的。
复制算法的最佳使用场景:对象的存活率较低。
标记清除算法:第一次扫描将活着的对象进行标记,第二次扫描没有标记的对象直接清除。
缺点:两次扫描浪费时间。会产生内存碎片。不需要额外的空间。
标记压缩:再次扫描,将存活的对象移到一段
缺点:虽然防止了碎片产生,但是任然浪费时间。
-
JMM
java Memory model
-
java内存模型
-
缓存一次性协议,用于定义数据读写的规则
JMM定义了线程和住内存之间的抽象关系:线程之间的共享变量存储在主内存中,内阁线程有一个私有本地内存。
Volatile:可见性,不保证原子性,禁止指令重排(使用内存屏障来实现的,store屏障将工作内存中的变量刷新到主内存和local屏障从主内存中读取共享变量。)
-
-
总结
栈:像一个手枪弹夹,先进先后出。不存在GC,只有线程运行结束才会释放。主要存放的是八大数据类型+对象的引用+实例的方法,管理线程生命周期和线程同步。
本地方法栈(Native):Native 指向的是底层语言编写的指令(汇编,c/c++语言),调用本地方法接口JNI,可以扩展java,融合其他语言。
程序计数器:是每个线程私有的,就是一个指针,用来指向方法区中方法的字节码(用来指向程序即将要指向的指令的下一个地址),占用的内存非常小,可以忽略不计。
方法区:被所有线程共享,所有字段的字节码,特殊的方法如构造函数,接口代码,所有定义的方法和信息都保存在方法区。静态常量+常量+类信息+运行时常量池。
堆:GC主要是在堆中存在,对象刚new出来存放在伊甸园区,经过轻gc放入幸存区from中,每次gc保证幸存区to中不存放对象,当然两个幸存区会相互切换,保证空的幸存区为幸存区to,经过15次gc还没有被回收的对象存放到永久区(1.7),1.8以后叫元空间。
可以设置次数 -XX:MaxTenuringThreshold