JVM知识点总结
jvm
方法区-》Medhod Area
栈-》Stack
本地方法栈-》Native Medhod Stack
堆-》Heap
jvm架构加载过程
对象实例化过程
完整代码:
//类加载器的反射获取
public class JVM01 {
//类是模板,对象是具体的
public static void main(String[] args) {
JVM01 jvm01 = new JVM01();
JVM01 jvm02 = new JVM01();
JVM01 jvm03 = new JVM01();
System.out.println(jvm01.hashCode());
System.out.println(jvm02.hashCode());
System.out.println(jvm03.hashCode());
Class<? extends JVM01> aClass1 = jvm01.getClass();
Class<? extends JVM01> aClass2 = jvm02.getClass();
Class<? extends JVM01> aClass3 = jvm03.getClass();
System.out.println(aClass1.hashCode());
System.out.println(aClass2.hashCode());
System.out.println(aClass3.hashCode());
}
}
双亲委派机制
自底向上检查类是否被加载,自顶向下执行加载器,所以最终执行的是Boot的加载器
App(应用加载器)-》Ext(拓展类加载器)-》Boot(根加载器)
package lang.String;
public class String {
public static void main(String[] args) {
System.out.println("asd");
}
}
沙箱安全机制
沙箱机制就是为了将java代码限制在jvm特定的运行环境中,严格防止代码对本地资源进行访问,防止对本地系统造成破坏
沙箱主要限制系统资源访问,例如:CPU、内存、文件系统、网络。不同级别的啥想对这些资源访问的限制也可以不一样
当前最新的安全机制实现,引入了域(Domain)的概念。虚拟机吧所有代码加载到不同的系统域和应用域,系统域部分专门负责与关键资源进行交互。而各个域应用部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域(Protected Domain),对应不一样的权限(Permission)。存在于不同域中的类文件就具有了当前域的全部权限,下图是最新安全模型
虚拟机将代码加载到不同的域,然后代码拥有了该域的所有权限,这样能控制不同代码拥有不同调用操作系统和本地资源的权限
例如递归造成jvm栈溢出
组成沙箱的基本条件
1.字节码校验器(bytecode verifier):确保Java类文件遵循Java语言规范。可以帮助Java程序实现内存保护 。核心类不经过字节码校验
2.类装载器:其中类装载器在3个方面对Java沙箱起作用
- 防止恶意代码干涉善意代码(双亲委派机制)
- 守护被信任的类库边界
- 它将代码归入保护域,确定了代码可以进行哪些操作
类装载器采用的机制是双亲委派机制:
1.从最内层JVM自带的类加载器开始加载,外层恶意同名类得不到加载从而无法调用
2.由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内部类,破坏代码就自然无法生效
3.存取控制器(access controller):存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定可以由用户指定。
4.安全管理器(security manager):是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高
5.安全软件包(security package):java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:
- 安全提供者
- 消息摘要
- 数字签名
- 加密
- 鉴别
Native(了解)
如果要使用java平台和其他本地代码接口进行交互,就要使用Java Native Interface(java本地接口)
例如java用户想用java平台调用本地c代码的api
JNI的诞生:想要立足,为了能够调用C,C++的程序
- JNI会进入到本地方法栈
- JNI一般用于java程序和硬件交互,类似于打印机
- JNI在内存区域开辟一块区域:native medhod stack
方法区
Medhod Area方法区,方法区被所有线程共享,所有字段和方法字节码
静态变量,常量,类信息(构造方法、接口定义),运行时的常量池存在方法区,但是实例变量存在堆内存中,和方法区无关
public class Jvm02 {
int a;
String b="123";
public static void main(String[] args) {
Jvm02 jvm02 = new Jvm02();
jvm02.b="321";
}
}
栈
栈:先进后出
队列:先进先出FIFO
栈内存,主管程序的运行,生命周期和线程同步;
线程结束,栈内存释放 ,对于栈来说,不存在垃圾回收问题,线程结束,栈结束
栈存储:8大基本类型+对象引用+实例的方法
栈帧:
堆
Heap,一个JVM只有一个堆内存,堆内存大小是可以调节的
类加载器读取了类文件后,一般会把类,方法,常量,变量保存我们所有引用类型的真实对象
堆内存细分为3个区域:
- 新生区(伊甸园区)
- 养老区
- 永久区-》在JDK8以后改名为(元空间)
GC垃圾回收,主要在新生区和养老区
假设内存满了,会报OOM(OutOfMemory),堆内存不够
新生区
- 伊甸园区-》满了会进入轻GC回收,存活的进入幸存者区
- 幸存者0区-》幸存者区和伊甸园区满了后,幸存者区存入养老区
- 幸存者1区
- 幸存者分为 from区和to区 to区永远为0,谁有值谁就是from区,默认标记到15进入养老区
养老区
永久区
这个区域常驻内存。用来存放JDK自身携带的class对象。interface元数据,存储的是java运行时的一些环境
一个启动类,加载了大量的第三方jar包。Tomcat部署了太多应用,大量动态生成的反射类。不断被加载。知道内存满,就会出现OOM
GC算法
引用计数法,复制算法,标记压缩清除法,可达性算法
复制算法
好处:没有内存碎片
坏处:浪费多一半空间,多了一半空间永远为to
复制算法最佳场景:对象存活度低,新生去最佳
标记压缩清除法
优点:不需要额外空间
缺点:需要两次扫描,严重浪费时间,会产生内存碎片
总结
内存效率:复制算法》标记清楚算法》标记压缩算法
内存整齐度:复制算法=标记压缩算法》标记清除算法
内存利用率:标记压缩算法=标记清除算法》复制算法
最合适的算法---------》分代收集算法(不同代用不同算法)
年轻代:
-
存活率低
-
复制算法
老年代:
- 存活率大
- 标记清除(内存碎片不是太多)+标记压缩混合
java内存模型:JMM
JMM定义了线程与内存之间的抽象关系