jvm 核心机制
一、 类加载器
作用 加载 class file 文件
类是模板抽象的 而对象是实际存在的
-
虚拟机自带的加载器
-
启动类加载器 根加载器
-
扩展类加载器
-
应用程序加载器
-
双亲委派机制
机制是 jvm会去一层一层找 如果上层有同样的方法 则会用上层的
应用程序加载器 app —>扩展加载器 exc—>boot加载器 (根加载器)
如果扩展加载器和根加载器都没有的情况下 才回去用到我们的应用加载器所实现的
- 类加载器收到类加载请求
- 将这个请求委托给父类加载去完成 一直向上委托 直到启动类加载器
- 启动类类加载器会检查是否能加载此类 如果可以就加载 执行结束 如果不行就抛出异常就向子类传递加载 直到能加载即可
二 沙箱安全机制
字节码校验器 确保java编写过程规范性 这样可以使java程序内存保护 核心类就不会经过字节码校验
类装载器(class loader):其中类装载器在三个方面对沙箱起作用
类装载器采用的就是双亲委派机制
三 native 方法区
1 native 介绍
public class Thread {
public static void main(String[] args) {
}
/**
* native 凡是加了native关键字的 说明java范围作用达不到了 会去调用c语言的库
* 会进入到本地方法栈 native method stack
* 调用本地方法接口 jnI
* JNI的作用就是扩展 java使用 融合不同的编程语言提供java使用
* java诞生时 c c++ 横行 想要立足必须要有调用c c++的程序
* 它在内存中专门开辟一块标记的区域 native method stack 登记native方法 而并不能执行
* 在最终加载本地方法库中的方法 通过JNI
*
* java程序驱动打印机 管理系统常见 roboot 企业很不常见
* 现在可以利用 通信实现 socket webservice http
*/
private native void start0();
}
2 pc寄存器
1 线程私有 就是一个指针 执向方法区的方法字节码 然后每次加1 是非常小的内存单元
3 方法区
method area
方法区是被线程共享 所有字段字节码 以及一些特殊方法 如构造函数,接口代码也在此定义 简单说 所有定义的方法信息都保存在该区域 此区域属于共享区间
静态常量 变量 类信息(构造方法 接口定义),运行时常量池存在方法区中 但是 实例变量存在堆内存中 和方法区无关static final class 常量池
jvm运行流程图
jvm有几种
hotspot 这个是我们常用的jdk自带的
BEA jrockit
IBM j9vm
堆
类加载器读取了类文件后 堆里有什么?
类 方法 常量 变量 以及引用的实例对象
GC垃圾回收主要存在 新生区和养老区 新生区垃圾回收(轻GC)养老区垃圾回收(重GC)
假设内存满了 就会报OOM错误 因为最后养老区也满了
JDK 8 以后 永久存储区改名为(元空间)
真理 经过研究 99%的对象都是临时的
永久区
这个区域内存常驻 用来存放jdk的class对象 和 interface元数据 存放java运行环境 这个区域不存在垃圾 vm关闭释放内存
- jdk 1.6之前: 永久代 常量池是在方法区
- jdk 1.7 :永久代 永久代 在慢慢退化 去永久化 常量池在堆里
- jdk 1.8之后 :无永久代 常量池在元空间里
可以手动对堆内存调优 -xms1024m -xms1024m -XX:+PrintGCDetail 这是将堆内存 最大和初始调为1024
(元空间 逻辑上存在 物理上其实是不存在的 “非堆”)
OOM报错 通常都是内存问题
- 调大内存
- 分析逻辑上内存的问题
package java.com;
import java.util.Random;
/**
* @author hp
制造死循环 出现oom
*/
public class demo1 {
public static void main(String[] args) {
String str="aasdsada";
while (true){
str+=str+ new Random().nextInt(1111111111)+new Random().nextInt(1111111);
}
}
}
/**
* 堆内存调优
* -xms1024m -xms1024m XX:+PrintGCDetail
* 改大堆内存
*/
public static void main(String[] args) {
long max = Runtime.getRuntime().maxMemory(); //最大内存
long total = Runtime.getRuntime().totalMemory();//初始化内存大小
System.out.println("max"+max+"字节\t"+(max/(double)1024/1024)+"mb");
System.out.println("total"+total+"字节\t"+(total/(double)1024/1024)+"mb");
}
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid18720.hprof ...
Heap dump file created [7855930 bytes in 0.014 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.Demo03.<init>(Demo03.java:9)
at com.Demo03.main(Demo03.java:15)
//jvm似乎能捕获 堆溢出的错误
public class Demo03 {
/**
xms 设置初始化内存大小 默认1/64
xmx 设置最大分配内存 1/4
-xx:+printGCDetails 打印gc
* -xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
*/
byte[] array=new byte[1*1024*1024]; //1m
public static void main(String[] args) {
ArrayList<Demo03> list=new ArrayList<>();
int count=0;
try {
while (true){
list.add(new Demo03()); //这个工具主要看线程 能定位到准确问题地方
count=count+1;
}
}catch (Exception e){
e.printstacktrace();
}
}
GC
GC主要作用于堆里
JVM在进行GC时,并不是对这三个区域统一回收。大部分时候回收都是新时代
- 新生区
- 幸存区(from to) 动态的会交替进行
- 老年区
GC两种类 轻 GC( 普通GC) 重GC(全局GC)
GC题目:
- jvm的内存模型和分区 详细每个区放什么?
- 堆里面分区有哪些 eden from to 老年区 说说他们的特点
- 特点:eden 特点就是创建对象都会在这里创建 from to动态替换 老年区存放在from to活下来的对象 】
- GC的算法有哪些? 标记清除法 标记压缩,复制算法,引用计数法 怎么用的
- 轻GC 重GC分别在什么时候发生
1.引用计数法
因为每个对象都要给一个计数器 所以非常耗费资源
2.复制算法
动态展示
其实就是把第一次存活的复制到 to 然后在把第一次from的存活的也复制到to 然后它自己为空 就变成了TO
- 好处:没有内存碎片 统一存放
- 坏处:浪费了内存空间 有一半空间永远为空 假设100%存活 极端
复制算法最佳使用 存活率较低的地方 新生区就是最佳使用场景
3.标记清除(压
(压缩)算法
- 好处 :不需要额外的空间
- 坏处 :两次扫描严重浪费时间 会产生垃圾碎片
压缩
先标记清理几次轻GC 在进行压缩 减少碎片
总结
内存效率:复制算法>标记清除算法>标记压缩算法(时间复杂度)
难道没有最优算法嘛?
答案:没有 只有最合适的算法----》GC:分代收集算法
年前代
- 存活率低
- 复制算法
老年代
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。