JVM基础篇1 - Class的加载
JVM基础篇2 - 指令集
JVM进阶篇1 - 内存模型
JVM进阶篇2 - GC垃圾回收
JVM总览- JVM架构
1.JVM架构图
第三方插件基本都是在 类加载器或执行引擎上动手
2. jvm的位置:
3. jvm结构体系
4. 类加载器: 加载Class文件
- 虚拟机自带的加载器
- 启动类 (根) 加载器
- 扩展类加载器
- 应用程序加载器
app—>扩展类(ext) ----> (启动类)根加载器(rt) —>
- 类加载器收到类加载的请求
- 将这个请求向上委托给父类加载器去完成, 一直向上委托, 直到启动类加载器(根加载器rt)
- 启动类加载器检查是否能够加载当前这个类, 能够加载就结束了, 使用当前加载器,否则抛出异常,通知子类加载器进行加载.
- 重复步骤3
双亲委派机制:
从上图中我们就更容易理解了,当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。那么有人就有下面这种疑问了?
为什么要设计这种机制?
这种设计有个好处是,如果有人想替换系统级别的类:String.java。篡改它的实现,在这种机制下这些系统的类已经被Bootstrap classLoader加载过了(为什么?因为当一个类需要加载的时候,最先去尝试加载的就是BootstrapClassLoader),所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。
5. 沙箱安全机制
Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络
。不同级别的沙箱对这些资源访问的限制也可以不一样。
组成沙箱的基本组件:
-
字节码校验器
(bytecode verifier):确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。 -
类装载器
(class loader):其中类装载器在3个方面对Java沙箱起作用
- 它防止恶意代码去干涉善意的代码;
- 它守护了被信任的类库边界;
- 它将代码归入保护域,确定了代码可以进行哪些操作。
虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由Java虚拟机为每一个类装载器维护的,它们互相之间甚至不可见。
类装载器采用的机制是双亲委派模式。
- 从最内层JVM自带类加载器开始加载,外层恶意同名类得不到加载从而无法使用;
- 由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内层类,破坏代码就自然无法生效。
-
存取控制器
(access controller):存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。 -
安全管理器
(security manager):是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。 -
安全软件包'
(security package):java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括: -
安全提供者
-
消息摘要
-
数字签名
-
加密
-
鉴别
6.native :凡是带了native关键字,说明java的作用范围达不到了,回去调用底层C语言的库
private native void start0();
- 会进入本地方法栈,调用本地方法,本地接口 JNI(java native interface)
- JNI的作用:扩展java的使用,融合不同的编程语言为java所用
- 他在内存区域中专门开辟了一块标记区域 :Native Method Stack 登记native方法,在最终执行的时候,加载本地方法库中的方法通过JNI
pc寄存器
Method Area 方法区
方法区是被所有线程共享的,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间;
静态变量(static),常量(final),类信息(Class构造方法,接口定义),运行时的常量池存在方法区中, 但是实例变量存在堆内存中,和方法区无关.
对象实例化过程:
JDK8 HotSpot JVM 使用本地内存来存储类元数据信息并称之为:元空间(Metaspace)
7. 堆:Heap, 一个JVM只有一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后,一般会把什么东西放到堆中?
类, 方法,常量,变量~,保存我们所有引用类型的真实对象;
堆内存中还要细分为三个区域:
●新生区(伊甸园区) Young/New
●养老区old
●永久区Perm(JDK1.8中已经被移除,被方法区的元空间取代)
GC垃圾回收,主要是在伊甸园区和养老区~
假设内存满了,OOM,堆内存不够! java.lang.OutOfMemoryError:Java heap space
永久存储区里存放的都是Java自带的 例如lang包中的类 如果不存在这些,Java就跑不起来了
在JDK8以后,永久存储区改了个名字(元空间)
新生区
●类诞生和成长的地方,甚至死亡;
●伊甸园,所有的对象都是在伊甸园区new出来的!
●幸存者区(0,1)(from区 to区)
伊甸园满了就触发轻GC,经过轻GC存活下来的就到了幸存者区,幸存者区满之后意味着新生区也满了,则触发重GC,经过重GC之后存活下来的就到了养老区。
真理:经过研究,99%的对象都是临时对象!|
永久区
这个区域常驻内存的。用来存放JDK自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境~ 这个区域不存在垃圾回收,关闭虚拟机就会释放内存
●jdk1.6之前:永久代,常量池是在方法区;
●jdk1.7:永久代,但是慢慢的退化了,去永久代,常量池在堆中
●jdk1.8之后:无永久代,常量池在元空间
元空间:逻辑上存在,物理上不存在 (因为存储在本地磁盘内) 所以最后并不算在JVM虚拟机内存中