JVM基础

JVM

类加载器

作用:加载Class文件 ---> new Student();

双亲委派机制

App ---> EXC ---> BOOT(最终执行)

Bootstrap classLoader: 主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。

ExtClassLoader: 主要负责加载jre/lib/ext目录下的一些扩展的jar。

AppClassLoader: 主要负责加载应用程序的主函数类

步骤

  1. 类加载器收到类加载的请求
  2. 将这个请求向上委托给父类加载器去完成,一直向上委托,直到根加载器(启动类加载器)
  3. 启动类加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器。否则,抛出异常,通知子加载器进行加载。
  4. 重复步骤3.

沙箱安全机制

java中的安全模型

​ 在Java中将执行程序分成本地代码和远程代码两种,本地代码默认视为可信任的,而远程代码则被看作是不受信的。对于授信的本地代码,可以访问一切本地资源。而对于非授信的远程代码在早期的Java实现中,安全依赖于沙箱 (Sandbox) 机制。

​ 但如此严格的安全机制也给程序的功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件时候,就无法实现。因此在后续的 Java1.1 版本中,针对安全机制做了改进,增加了安全策略,允许用户指定代码对本地资源的访问权限。

​ 在 Java1.2 版本中,再次改进了安全机制,增加了代码签名。不论本地代码或是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。

​ 当前最新的安全机制实现,则引入了域 (Domain) 的概念。虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域 (Protected Domain),对应不一样的权限 (Permission)。存在于不同域中的类文件就具有了当前域的全部权限。

Native关键字

native:凡是带了native关键字的,说明java的作用范围已经达不到了,回去调用底层C/C++的库。

带了native关键字的方法会进入本地方法栈,调用本地方法本地接口---JNI(Java Native Interface)

JNI作用:扩展java的使用,融合不同的编程语言为java所用!最初是为了调用C/C++

Java在内存区域中专门开辟了一块标记区域:Native Method Stack,登记 native 方法,在最终执行的时候,通过JNI加载本地方法库中的方法

PC寄存器(程序计数器 Program Counter Rediser)

​ 每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向像一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计

方法区

Method Area 方法区

方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说。所有定义的方法的信息都保存在该区域,此区域属于共享区间;

静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关

方法区:static、final、Class、常量池

栈:栈内存,主管程序的运行,生命周期和线程同步,线程结束,栈内存也就是释放。所以对于栈来说,不存在垃圾回收问题,线程结束,栈就销毁。栈是线程级的,不只一个

栈中存放的东西:8大基本类型 + 对象的引用 + 对象(实例)的方法

栈运行原理

栈帧

栈+堆+方法区:交互关系

三种JVM

  • Sun公司 HotSpot(在用)
  • BEA JRockit
  • IBM J9 VM

Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的

类加载器读取了类文件后,一般会把什么东西放到堆中?

类、方法、常量、变量,保存我们所有引用类型的真实对象;

堆内存中还要细分为三个区域:

  • 新生区 (伊甸园区【Eden Space】)Young/New
  • 养老区 old
  • 永久区 Perm

GC回收,主要是在伊甸园区和养老区

假设内存满了,OOM(堆内存不够,满了)(几乎见不到,因为大部分对象都是临时对象,在伊甸园区和幸存者区就被清除了)

JDK8之后,永久存储区改名为 ===> 元空间

新生区

是类诞生 和 成长的地方、甚至死亡:

  • 伊甸园区:所有对象都是在这里被new出来的
  • 幸存者区(0,1):在伊甸园区占满时会执行轻GC,会有“侥幸”活下来的对象进入改区

经过研究,99%的对象都是临时对象!

老年区

新生区中侥幸存活的会进入老年区

永久区(元空间)

堆内存空间

元空间 只在逻辑上存在,物理上不存在

元空间又名非堆,属于堆,有一部分人认为非堆不属于堆

如果遇到OOM(堆内存满)的解决方式

  1. 先手动扩大堆内存

    • 代码:-Xms1024m -Xmx1024m -XX:+PrintGCDetails
  2. 分析内存,看一下哪个地方出错

  3. 如果依然报错,可能需要回去查看代码 是否存在死循环代码等等。。。

GC(垃圾回收)

GC的作用区域

方法区 和 堆 内,方法区属于元空间,元空间属于堆,栈中没有垃圾回收

JVM在进行GC时,并不是对这三个区域统一回收。大部分时候,回收都是新生代

  • 新生代
  • 幸存区(form,to)
  • 老年区

GC两种类

  • 轻GC(普通的GC)
  • 重GC(全局GC)

GC算法

引用计数法

低效,一般不用

复制算法

幸存区又被成为 form 和 to 或者 0,1,幸存者区是会不断互相交换的。哪个是空的,哪个就是to

当幸存区交互超过15次时,垃圾就会被丢到养老区

  • 好处:没有内存的碎片·
  • 坏处:浪费了内存空间·:多了一半的空间永远是空to。

复制算法最佳使用场景:对象存活度较低的时候:新生区··

标记清除算法

  1. 对需要的对象进行扫描:对对象进行标记
  2. 清除:对没有标记的对象,进行清除

缺点:

  • 两次扫描,严重浪费时间,会产生内存碎片

优点:

  • 不需要额外的空间!

标记压缩算法

  • 防止内存碎片产生,再次扫描,向一端移动存活的对象,但是多了一个移动成本

总结

内存效率:复制 > 标记清除 > 标记压缩

内存整齐度: 复制 = 标记压缩 > 标记清除

内存利用率:标记压缩 = 标记清除 > 复制

没有最优的算法,只有最合适的算法-------->GC:分代收集算法

  • 年轻代:
    • 存活率低,使用复制算法
  • 老年代:
    • 区域大:存活率高,标记清除算法(内存碎片不是大多)+标记压缩混合实现。

JMM:Java Memory Model(Java内存模型)

  1. 什么是JMM
    • 允许编译器和缓存以数据在处理器特定的缓存(或寄存器)和主存之间移动的次序拥有重要的特权,除非程序员使用了volatile或synchronized明确请求了某些可见性的保证。
  2. 。。。。。。``
posted @ 2021-12-25 21:17  Indigo。  阅读(171)  评论(0)    收藏  举报