JVM体系结构
类加载器
作用:加载class文件
分类:
- 虚拟机自带的加载器
- 启动类(根)加载器
- 扩展类加载器
- 应用程序加载器
双亲委派机制
- 类加载器接收到类的加载请求
- 将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器
- 启动类加载器检查是否能加载当前这个类,能加载就结束,使用当前的加载器,否则抛出异常,通知子类进行加载
- 重复步骤3
案例:自己创建一个java.lang包,并且在里面创建一个String类,重写toString方法,调用,会出现错误,因为类加载器根据双亲委派机制会找到扩展类加载器加载,而自己写的应用用应用程序加载器加载
沙箱安全机制
字节码校验器
类加载器
存储控制器
安全管理器
安全软件包
native关键字
- 凡是带了native关键字的说明Java的作用范围达不到了回去调了底层C的库
- 首先会进入本地方法栈
- 然后通过本地方法接口JNI(拓展java的使用,融合不同的编程语言为Java所用)
- 在内存区域专门开辟一块标记区域Native Method Stack登记native方法
- 在最终执行的时候,通过JNI加载本地方法库中的方法
PC寄存器
程序计数器
每一个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,即将要执行的指令代码)在执行引擎读取下一条指令,是一个非常小的空间,可以忽略不计
方法区
方法区是被所有线程共享,所有字段和方法字节码以及一些特殊的方法如构造函数接口代码也在此定义,简单说所有的定义的方法的信息都存在此区域,此区域属于共享空间。
静态变量、常量、类信息(构造函数,接口定义)运行时的常量池都存在此区域,但是实例变量存在堆内存中,与该区域无关
栈
main方法为什么先执行,最后结束?
Java中每执行一个方法都会将其压入栈内,方法执行结束,将方法弹出,在程序运行时首先会将main方法压入栈内,然后压入其它方法,其它方法执行结束依次弹出,最后弹出main方法,如果我们此时有两个方法A和B,在A中调用B方法,在B中调用A方法,这样在程序运行过程中会出现,A和B一直压入栈,而不出栈,最终导致栈溢出。
堆
调节堆的初始内存和总内存 -Xms1024m -Xmx1024m
元空间:逻辑上存在,物理上不存在
GC垃圾回收
两种回收方式
- 轻GC (针对新生区)
- 重GC(full GC 针对老年区)
引用计数法
给每个对象分配一个计数器,用于记录对象的使用次数,假如说一个对象的使用次数是0,在进行GC垃圾回收的时候就对其进行清除。
复制算法
谁空谁是to,在进行一次GC垃圾回收时,Eden 区存活的将进入幸存区的to区,如果此时from区中存在存活对象,则复制到to区,此时,from区为空,因此to区变成了from区,from区变成了to区。经过GC后必然保证了Eden和to区为空。当对象经历了15次form和to区之间的交换后还存活,则将进入老年区,15可以更改 -XX:MaxTenuringThreshold=5
好处:没有内存碎片
坏处:浪费了内存空间,多了一半的空间永远是空的(to区)
最佳使用场景:对象存活度较低的时候
标记清除算法
回收时对存活的对象进行标记,清除时对没有标记的对象进行清除
优点:
- 不需要额外的空间
缺点:
- 两次扫描,浪费时间
- 产生内存碎片
标记整理(标记压缩)
防止内存碎片,对内存碎片进行整理
标记清除压缩
先进性标记清除几次,产生较多的内存碎片,在进行一次标记整理
总结
- 内存效率(时间复杂度):复制算法>标记清除算法>标记整理算法
- 内存整齐度:复制算法=标记压缩算法>标记清除算法
- 内存利用率:标记压缩算法=标记清除算法>复制算法
新生区,存活率低,适用于复制算法
老年区,适用于标记清除压缩算法