1.学习方法
process on可以搜jvm看图
谈谈你对jvm的理解,java8虚拟机和之前的变化更新?....
2.jvm的位置
1.jre包括jvm(是个软件)可以安装在<-----windows linux mac… <—硬件体系(intel,spac)
2. .java—> classFile—>类加载器Class Loader
<—> jvm—>运行时数据区(Runtime Data Area)
包括(
方法区method area(一定有垃圾)(特殊的堆)
java栈 stack
本地方法栈 native method stack
堆 heap(一定有垃圾)(99%jvm调优在这里)
程序计数器pc
)
<—> (同级)执行引擎
<----->(同级)本地方法接口 <—本地方法库
3.类加载器
1.作用 加载class文件(把class文件加载进来,初始化这个对象)
2.过程 new Car(); 具体数据在堆 , 引用地址在栈,通过引用地址找到堆中的数据
Car car1=new Car();
Car car2=new Car();
//对象不相同
sout(car1.hashCode())
sout(car2.hashCode())
#得到类加载器的模板
Class<? extends Car> aClass1=cart1.getClass();
Class<? extends Car> aClass1=cart1.getClass();
//对象相同,因为是相同的一个类模板
sout(aClass1.hashCode())
sout(aClass2.hashCode())
(图2)
3.加载器的分类
1. rt.jar resources.jar启动类bootstrap root(根)加载器 rt.jar大厂改这个包 实现自定义功能
2.ex.jar 拓展类加载器
3.应用程序application(系统)加载器 虚拟机自带加载器 负责加载用户类路径 classpath 上所有的 jar 包和 .class 文件。
4.自定义加载器 User ClassLoader
Class<? extends Car> aClass1=cart1.getClass();
ClassLoader cl= aClass1.getClassLoader();//application加载器
sout(cl.getParent());//拓展类
sout(cl.getParent().getParent());//null,c++写的获取不到
//改写rt.jar,下面写会报不存在main方法异常
//双亲委派机制保证安全执行代码,运行时会去加载器找
// APP->EXC-->BOOT(最终执行),确保这几个都有这个对象
//如果boot没有这个类,则倒着找 BOOT--->EXC--->APP
package java.lang;
publc class String{
public String toString(){
return "aaa";
}
main(){String string=new String();}
}
//创建一个student类,在application系统加载器使用
4.双亲委派机制过程(一次找缓存,一次找在父类有没有这个对象)
1.类加载器收到类加载请求
2.请求向上委托父类加载器去完成,一直向上委托,直到到启动类加载器
3.启动类加载器如果能加载这个类,加载后结束,否则抛出异常通知子加载器加载
4.重复步骤3
Class Not Found:就是在加载器都找不到类
Java: C++-- 去指针,去内存管理
本地方法就是调用c++的方法
如: new Thread.start(); //后面调用了 private native start0();看不懂的方法
如图(2)
4.沙箱安全机制(本地代码需要权限信任,远程代码不受信任,也需要权限)(非重点)
- java1.1增加了安全策略(需要权限)
- java1.2增加的 升级为 代码签名(java生成https文件)
- jdk 1.6 最新安全模型(保护域),每个组都有权限,防jvm溢出,不断写文件也会StackOverFlowError
class Test{
main(){new Test().a();}
public void a(){b()}
public void b(){a()}
}
组成部分
1.字节码校验器 bytecode verifier 确保java类文件符合java语法规范,实现
java查询内存保护,并不是所有类文件都会经过字节码校验器校验,比如核心类
(java运行时异常,语法错误)
2.类加载器 的作用
防止恶意代码干涉善意代码
守护信任类库的边界
将代码归入保护域,规定代码有哪些操作 new Robot();可以写外挂
包括1.存取控制器 access controller控制核心api对操作系统的存取权限
2.安全管理器 security manager
3.安全软件包 security package
数字证书keytools https证书
加密
5.native(面试重点),方法区
class Demo{
main(){
new Thread(()->{sou("xxx").start();})
}
}
//单纯的class可以写,凡是带native关键字就不是java方法(c++,python....)了,调用本地接口jni(java native interface)
//会进入本地方法栈(登记native方法) 图3,在最终执行的时候执行
private native void start0(); //native很少用,一般用于硬件,或者写外挂,企业级应用少见 //调用其他语言 Socket WebService http
6.程序计数器 program counter register 每个线程都有一个程序计数器是线程私有的,就是一个指针指向方法区的方法字节码(要执行指令的地址)
非常小的内存空间,读取下一条指令 进行+1,帮助栈井井有条
7.方法区 被所有线程共享,所有字段和方法字节码,构造函数,接口代码(有抽象方法)
!!!静态常量 常量 类信息(构造方法,接口定义),运行时的常量池存储方法区中.但是实例变量存在堆内存中与方法区无关(static final,Class,常量池)
图j3 赋初值就还是在堆拿,没有就去常量池拿
包括 8大基本类型 对象引用,实例方法
栈帧(编译原理): 有栈顶和栈低都有,程序正在执行的方法一定在栈顶,子帧调用父帧,父帧调用子帧,相互调用
如图jvm4
队列:先进先出(FIFO) 程序 =数据结构+算法 : 程序=框架+业务逻辑 :吃饭
喝多了图就是栈,吃多了拉就是队列
例子: main方法调用Test.test()的方法
stack
| test() |
|main()|
~~~~~
main方法调用Test.test()的方法又调a()方法,又 a()调test()方法… | test() …| 栈溢出
| a() | | test() |
|main()|
~~~~~
new对象的过程
ClassLoader load Class Object to jvm 加载
becoming Class model 变成Class模板--->
give off an address to |stack| 分配地址给栈--->
stack point to heap(heap has consitant in constant pool) 指向堆-->
return address and constant to Class Obj 返回地址和常量给Class
如图jvm5
9.虚拟机父类和堆(!!!)
虚拟机的分类 了解
bea的JRockIt 适合军事办公,最快的jvm
ibm的j9vm(绑定了硬件)
hotspot(sun)主要使用
java -version
//heap一个jvm只有一个堆内存 大小可以调节
//ideaproject旁边有个 运行的旁边,可以调节jvm传入的参数
// vm options
1. 什么东西放到堆中?类 方法 常量 变量 引用类型的真实对象(大量的东西)
//如果栈的引用对象失效,堆中的没有被引用的对象就是垃圾
堆内存有3个区域 主要在伊甸园和养老区
新生区(新生代)(伊甸园yidian) young/new,轻量级gc,清理的垃圾很小
1. 伊甸园[Eden Space]
2.幸存区0区(不停与1区交换,不停的从伊甸园淘汰,在这里也在淘汰)
3.幸存区1区
养老区 (老年代)old ,重gc,full gc 清理的垃圾很大,新生区满了就清这里永久存储区(持久代) (perm)anent jvm内置的对象,需要永久存储的东西
3.字符串可以无限长(堆内存满了), OOM错误 OutOfMemoryError 内存满了
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
main(){
String a="xxx";
while(true){
a+=a+ new Random().nextInt(88888);
}
}
4.jdk8以后,永久存储区有个名字叫元空间
10.堆详解
1.新生区 : 类诞生和成长,甚至死亡的地方
伊甸园: 所有对象在伊甸园群生成的,满了 轻gc,如果这里对象存在引用就到下一个幸存者0,1区,伊甸园又可以存多个对象
养老区: 满了,就OOM错误
2.真理 99%的对象都是临时对象,通常不用while让他活到养老区
3.永久区(元空间),用来存放jdk自身class对象的,interface元数据,存放jre java运行时的环境或类信息(不存在垃圾回收) 非堆,逻辑上存在,物理上不存在,因为 新生代+老年代=我们设置总大小空间
应用场景:一个启动类加载了大量的第三方jar包,tomcat部署很多应用在webapp中,
大量动态生成反射类,不断被加载
jdk1.6之前 永久代, 常量池在方法区
1.7 永久代, 慢慢退化,去永久代,常量池在堆中
1.8 无永久代,常量池在元空间
如图: jvm6
代码查看内存:
main(){
//试图用的最大内存,单位字节 1g=1024m
long max =Runtime.getRunTime().maxMemory();
//初始化的总内存
long total =Runtime.getRunTime().maxMemory();
//我的电脑是16g的内存 16000mb
sout("max:"+(max/(double)1024/1024)+"mb"); //3605.5M ,最大可用, 1/4
sout("total:"+(total/(double)1024/1024)+"mb");//243.5M ,是固定的, 就是最低使用的内存总数, 1/64
//默认情况下,分配的总内存是电脑内存的 1/4 初始化的内存是 总内存的 1/64
//调参数 VM options ,不能太大.参数之间没有空格
//打印垃圾回收的内存
-Xms1024m -Xmx1024m -XX:+PrintGCDetails
//查看打印有
1.PSYoungGen psParallelCompact psParallelCompact ps并行压缩
2.eden from to
3.ParOldGen
4.Metaspace() //也叫非堆,因为 10g=新生区+老年区
}
4.(面试)你遇到过OOM错误吗
-----1.尝试扩大堆内存查看结果
-----2.分析内存,用工具查看那个地方的代码导致堆满了