JVM的结构
类加载器
作用:加载Class文件
1.虚拟机带的加载器
2.启动类(根)加载器
3.扩展类加载器
4.应用程序(系统)加载器
双亲委派机制
双亲委派的工作过程:上委托查找,向下委托加载 APP --> EXT--> BOOT
当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此, 因此所有的加载请求都应该传送到启动类加载其中
- 类加载器收到类加载的请求! 类加载器收到类加载的请求!
- 将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器
- 启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则,抛出异常,通知子加载器进行加载
- 最终找不到该类会报:Class Not Found Exception异常
Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。
ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。
AppClassLoader:主要负责加载应用程序的主函数类
沙箱安全机制
组成沙箱的基本组件:
- 字节码校验器(bytecode verifier)︰确保lava类文件遵循ava语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。
- 类装载器(class loader) :其中类装载器在3个方面对Java沙箱起作用
- 它防止恶意代码去干涉善意的代码; ...它防止恶意代码去干涉善意的代码;
- 它守护了被信任的类库边界; ...它守护了被信任的类库边界;
- 它将代码归入保护域,确定了代码可以进行哪些操作。
虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由Java虚拟机为每一个类装载器维护的,它们互相之间甚至不可见。
类装载器采用的机制是双亲委派模式。
1.从最内层VM自带类加载器开始加载,外层恶意同名类得不到加载从而无法使用;
2.由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内层类,破坏代码就自然无法生效。
- 存取控制器(access controller)︰存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。
- 安全管理器(security manager)︰是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。
- ·安全软件包(security package) : java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:
- 安全提供者。
- 消息摘要
- 数字签名keytools
- 加密
- 鉴别
Native:
package Native;
public class TestNative {
public static void main(String[] args) {
new Thread(()->{
},"my thread neme").start();
}
// native: 凡是带了native关键字的,说明java 的作用范围达不到了.会去调用底层C'语言的库
// 凡是带了native关键字的会进入本地方法栈 ---> 调用本地方法接口 JNI
// JNI作用:扩展JAVA的使用,融合不同的语言为JAVA所用 , 最初像融合C;C++
// JVM内存中为native专门开辟了一块标记区域: Native Method Stack,开辟的作用是为了登记natice方法
// 在最终执行的时候去加载本地方法库中的方法,通过JNI(本地方法接口)
// 现在调用其他接口: Socket;WebService HTTP
private native void start0();
}
.
PC寄存器 :
程序计数器: Program Counter Register
每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向像一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计
方法区:
Method Area方法区
方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间
静态变量(Static)、常量(Final)、类信息(Class)(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关
栈:数据结构
栈:先进后出,后进先出,相当于一个桶,先进的在最底下
栈就是一个特殊的线性表,数据先入后出,类似一个桶,只能对顶端进行操作,数据添加和取出分别叫做入栈和出栈(FIFO:First In First Out)
栈:栈内存,主管程序的运行,生命周期和线程同步,线程结束,栈内存也就释放了,对于栈内存不存在垃圾回收的概念,一旦线程结束,栈也会结束
栈存储:8种基本类型+队形引用+实例方法
栈运行原理:栈帧 ;栈顶,栈底,栈如果满了会StackOverflowError
栈;堆;方法区的交互关系:
三种JVM:
- Sun公司HotSpot Java Hotspot™ 64-Bit Server VM (build 25.181-b13,mixed mode)
- BEA JRockit
- IBM J9VM
堆:
Heap,一个JVM只有一个堆内存,堆内存大小可以调节
类加载器读取类文件后,一般会将 类,方法,常量,变量,引用类型的真实对象,放入堆中
堆内存还细分三个区域:
- 新生区(伊甸区)
- 老年区
- 永久区
javaGC的回收机制+清理算法_(illusion)的博客-CSDN博客
假设内存满了会报OOM错误
新生区:
类的诞生,成长,死亡 的地方
伊甸园:所有的对象都是在伊甸园区new出来的
幸存区:(S0,S1)
如果新生的对象无法在 Eden 区创建(Eden 区无法容纳) 就会触发一次Young GC,此时会将 S0 区与Eden 区的对象一起进行可达性分析,找出活跃的对象
将它复制到 S1 区并且将S0区域和 Eden 区的对象给清空,这样那些不可达的对象进行清除,并且将S0 区 和 S1区交换。
对象在伊甸园放不下将会执行一次轻GC,清理幸存区直到能放下对象,如果幸存区清理不出来,任然放不下对象,则会发生重GC,将清理幸存者区并且将幸存区存活下来的对象移到养老区,
如果清理过后幸存区域任然放不下对象,则重GC会将对象直接放在养老区中,如果养老区也不足以放下该对象,则会产生OOM。
永久区:
区域常驻内存的。用来存放JDK自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境或言息~,这个区域不存在垃圾回收!关闭VM虚拟就会释放这个区域的内存~
jdk1.6之前︰永久代,常量池是在方法区;
jdk1.7:永久代,但是慢慢的退化了,去永久代,常量池在堆中
jdk1.8之后∶无永久代,常量池在元空间
-Xms1024m -Xmx1024m -XX:+PrintGCDetails 调节初始内存总内存参数并打印信息
package Native;
public class Demo {
public static void main(String[] args) {
// 返回虚拟机试图使用的最大内存
long max = Runtime.getRuntime().maxMemory(); //返回字节 1024*1024
// 返回虚拟机总内存
long all = Runtime.getRuntime().totalMemory();
System.out.println("max:"+max+"字节\t"+(max/(double)1024/1024)+"MB");
System.out.println("ll:"+all+"字节\t"+(all/(double)1024/1024)+"MB");
}
// -Xms1024m -Xmx1024m -XX:+PrintGCDetails 调节初始内存/`总内存参数并打印信息
}
//输出
/*
D:\Development_kit\java_file\bin\java.exe -Xms1024m -Xmx1024m -XX:+PrintGCDetails "-javaagent:D:\Development_kit\idea\IntelliJ IDEA 2019.1.4\lib\idea_rt.jar=65133:D:\Development_kit\idea\IntelliJ IDEA 2019.1.4\bin" -Dfile.encoding=UTF-8 -classpath D:\Development_kit\java_file\jre\lib\charsets.jar;D:\Development_kit\java_file\jre\lib\deploy.jar;D:\Development_kit\java_file\jre\lib\ext\access-bridge-64.jar;D:\Development_kit\java_file\jre\lib\ext\cldrdata.jar;D:\Development_kit\java_file\jre\lib\ext\dnsns.jar;D:\Development_kit\java_file\jre\lib\ext\jaccess.jar;D:\Development_kit\java_file\jre\lib\ext\jfxrt.jar;D:\Development_kit\java_file\jre\lib\ext\localedata.jar;D:\Development_kit\java_file\jre\lib\ext\nashorn.jar;D:\Development_kit\java_file\jre\lib\ext\sunec.jar;D:\Development_kit\java_file\jre\lib\ext\sunjce_provider.jar;D:\Development_kit\java_file\jre\lib\ext\sunmscapi.jar;D:\Development_kit\java_file\jre\lib\ext\sunpkcs11.jar;D:\Development_kit\java_file\jre\lib\ext\zipfs.jar;D:\Development_kit\java_file\jre\lib\javaws.jar;D:\Development_kit\java_file\jre\lib\jce.jar;D:\Development_kit\java_file\jre\lib\jfr.jar;D:\Development_kit\java_file\jre\lib\jfxswt.jar;D:\Development_kit\java_file\jre\lib\jsse.jar;D:\Development_kit\java_file\jre\lib\management-agent.jar;D:\Development_kit\java_file\jre\lib\plugin.jar;D:\Development_kit\java_file\jre\lib\resources.jar;D:\Development_kit\java_file\jre\lib\rt.jar;D:\Development_kit\idea\com-huixin\comnetwork\target\classes Native.Demo
max:1029177344字节 981.5MB
ll:1029177344字节 981.5MB
Heap
PSYoungGen total 305664K, used 26214K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)
eden space 262144K, 10% used [0x00000000eab00000,0x00000000ec499be8,0x00000000fab00000)
from space 43520K, 0% used [0x00000000fd580000,0x00000000fd580000,0x0000000100000000)
to space 43520K, 0% used [0x00000000fab00000,0x00000000fab00000,0x00000000fd580000)
ParOldGen total 699392K, used 0K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)
object space 699392K, 0% used [0x00000000c0000000,0x00000000c0000000,0x00000000eab00000)
Metaspace used 3256K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 353K, capacity 388K, committed 512K, reserved 1048576K
Process finished with exit code 0
*/
package Native;
import java.util.ArrayList;
//-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError 打印oom DUMP
//Xms:设置初始化内存分配大小 1/64
//Xmx:最大分配内存 1/4
//-XX:+PrintGCDetails 打印GC垃圾回收信息
public class Demo2 {
byte[] array = new byte[1 * 1024 * 1024];
public static void main(String[] args) {
ArrayList<Demo2> list = new ArrayList<>();
int count = 0;
try {
while (true) {
list.add(new Demo2());
count++;
}
} catch (Error e) {
System.out.println("count:" + count);
e.printStackTrace();
}
}
}
在src下找到.hprof文件双击打开,分析异常信息
GC:
JVM在GC时,并不是对三个区域统一回收,回收的大部分都是新生代(E区)
- 新生代
- 幸存区S区 (from , to)
- Old区
GC分两种:轻GC(minor gc) 重gc(full gc)
四大算法应该为:复制算法,标记整理算法,标记清除算法,分代收集算法
引用计数法是对象被引用了计数器+1,引用结束-1,计数器为0就会被回收
幸存区:采用复制算法
好处:没有内存碎片
坏处:需要两倍内存空间,另一块S区永远时空的
标记清理:
优点:不需要额外空间
缺点:两次扫描.浪费时间,会产生内存碎片
标记整理:
优点:没有碎片空间
缺点: 需要依次向前移动