1.JVM的位置
JVM是运行在操作系统之上
2.JVM体系结构
Java程序代码--->Class File-->类装载器--->运行时数据区--->执行引擎--->本地方法接口--->本地方法库
运行时数据区包括:方法区,JAVA栈,堆,本地方法栈,程序计数器
垃圾回收不存在于:JAVA栈,本地方法栈,程序计数器
JVM调优:一般是在操作 方法区和堆
3.类加载器
作用:加载Class文件
类是模板,对象是具体的
-
虚拟机自带的加载器
-
启动类(根)加载器
-
拓展类加载器
-
应用程序加载器
4.双亲委派机制
-
类加载器收到类的加载请求
-
将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器。
-
启动类加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则,抛出异常通知子加载器进行加载
-
重复步骤
5.沙箱安全机制
组成沙箱的基本组件:
-
字节码校验器(bytecode verifier)︰确保Java类文件遵循]ava语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。
-
类装载器(class loader) :其中类装载器在3个方面对Java沙箱起作用
-
它防止恶意代码去干涉善意的代码;
-
它守护了被信任的类库边界;
-
它将代码归入保护域,确定了代码可以进行哪些操作。
-
虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由ava虚拟机为每一个类装载器维护的,它们互相之间甚至不可见。
类装载器采用的机制是双亲委派模式。
-
从最内层VM自带类加载器开始加载,外层恶意同名类得不到加载从而无法使用;
-
由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内层类,破坏代码就自然无法生效。
-
存取控制器 (access controller)︰存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。
-
安全管理器 (security manager)︰是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。
-
安全软件包(security package) : java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:
-
安全提供者
-
消息摘要
-
数字签名
-
加密
-
鉴别
-
6.Native
凡是带有native关键字的,说明Java的范围大部到了,会去调用底层C语言的的库!
会进入本地方法栈
调用本地方法接口 JNI
JNI作用:拓展JAVA的使用,融合不同的编程语言为Java所用!
当调用含有native关键字所修饰的方法时,会调用本地方法栈,再去找本地方法接口
Native Method Stack 它的具体做法是Native Method Stack 中登记native方法,在(Execution Engine )执行引擎执行的时候加载Native Libraies。【本地库】
7.pc寄存器
程序计数器:Program Counter Register 每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向像一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计
8.方法区
Method Area方法区 方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间; 静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关
方法区:static、 final、Class、常量池
9.栈
喝多了吐就是栈,吃多了拉就是队列
栈:栈内存。主管程序的运行,生命周期和线程同步;
线程结束,栈内存也就释放了,对于栈来说不存在垃圾回收机制问题,一旦线程结束,栈就Over。
栈:8大基本类型+对象引用+实例的方法
10.三种JVM
-
HotSpot
-
JRockit
-
J9VM
11.堆
一个JVM只有一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后,一般会把什么东西放到堆中? 类、方法 、常量、变量、保存我们所有引用类型的真实对象;
垃圾回收分为轻量级的垃圾回收和重量级垃圾回收
轻量级垃圾回收一般是指执行在新生区,重量级垃圾回收执行在养老区
堆内存中分为三个区域:
-
新生区:
-
幸存者区(0、1)
-
伊甸园区(所有的对象都是在伊甸园区new出来的)
-
类诞生和成长的地方甚至死亡
-
-
养老区
-
永久区(元空间)这个区域常驻内存的,用来存放JDK自身携带的Class对象、存储的是Java运行时的一些环境或类信息,这个区域不存在垃圾回收机制,关闭VM虚拟机就会释放这个区域的内存。
-
什么情况会导致在永久区就崩溃:一个启动类加载了大量的第三方jar包。tomcat部署了太多的应用,大量生成的反射类不断被加载,直到内存满,就会出现OOM;
-
jdk1.6:永久代,常量池是在方法区
-
jdk1.7:永久代,但是慢慢退化了,去永久代,常量池在堆中
-
jdk1.8:无永久代,常量池在元空间
-
12.新生区,老年区
13.永久区
14.堆内存调优
15.GC(常用算法)
GC的作用区域只有方法区和堆
-
新生代
-
幸存区(from、to)
-
老年区
GC两种类型:
-
轻GC:只针对于新生代,偶尔清理幸存区
-
重GC:全局清理
GC常用算法:
GC题目:
-
JVM的内存模型和分区~详细到每个区放什么?
-
堆里面的分区有那些?Eden、from、to、老年区、说说他们的特点
-
GC算法有哪些:标记清除法、标记压缩法、复制算法、引用计数法,怎么使用?
-
轻GC和重GC分别在什么时候发生?
引用计数法:
对每一个对象进行计数,使用过的类计数数量加一,将计数数最小的类进行释放
复制算法:
它是使用在新生代中的幸存to区和幸存from区,Eden区进行轻GC时幸存的对象就会被复制算法复制到幸存to区,Eden区会被清空,这时幸存to区和幸存from区进行交换,空的区为幸存to区,另一个幸存from区,当再次进行轻GC时会将Eden区幸存的对象复制到幸存to区,先将幸存to区幸存的对象复制到幸存from区 ,幸存to区和幸存from区进行交换,空的区又为为幸存to区;
当一个对象经历15次轻GC还未被清除就能进入老年区
好处:没有内存的碎片
坏处:浪费了内存空间:多了一半是幸存区to,假设对象100%存活(极端情况)
复制算法最佳的使用场景:对象存活度偏低的时候:新生区
标记清除法:
扫描对象:对活着的对象进行标记
清除:对没有标记的对象进行清除
优点:不需要额外的空间
缺点:两次扫描,严重浪费时间。会产生内存碎片。
标记压缩法:
是对标记清除的再优化
压缩:防止内存碎片的产生,再次扫描,向一端移动存活的对象,多了一个移动成本
总结
内存效率:复制算法>标记清除算法>标记压缩算法(时间复杂度)
内存整齐度:标记压缩算法=复制算法>标记清除算法
内存利用率:标记压缩算法=标记清除算法>复制算法
年轻代:
-
存活率低
-
复制算法
老年代:
-
区域大:存活率高
-
标记清除(内存碎片不是太多)+标记压缩混合实现
16.JMM
JMM:java内存模型
JMM的作用:缓存一致性协议,用于定义数据的读写规则, 解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题
-
缓存一致性问题其实就是可见性问题。
-
处理器优化是可以导致原子性问题
-
指令重排即会导致有序性问题
解决共享对象可见性问题:volatile
数据同步八大原子操作
(1)lock(锁定):作用于主内存的变量,把一个变量标记为一条线程独占状态
(2)unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
(3)read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
(4)load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中
(5)use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎
(6)assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量
(7)store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作
(8)write(写入):作用于工作内存的变量,它把store操作从工作内存中的一个变量的值传送到主内存的变量中