简介:JVM有很多个不同版本的实现;其中HotSpot VM是最主流使用的JVM;Oracle官方jdk和开源openjdk都是使用这个JVM
JVM内存区域划分
JVM内存区域划分:按每个区域不同功能划分;相互之间不会干扰
本地方法栈
native:表示是JVM内部C++代码;给调用JVM内部方法准备的栈空间。存储本地方法native之间调用相关的信息。每个线程都有一份;还会有其它方法参数、返回地址、局部变量等等很多元素。
程序计数器
记录当前线程执行到哪个指令;每一个线程都有一份
虚拟机栈(和本地方法栈类似)
给java代码使用的栈;存的是方法之间的调用关系;还会有其它方法参数、返回地址、局部变量等等元素。也是一个线程有一份。
栈帧:每一个方法里对应一个栈帧;里面存的这个方法相关的东西。这里栈帧的空间是连续的(JVM有参数可以设置栈空间大小;超过范围就会栈溢出;栈溢出指单个的栈帧溢出;死递归就非常容易栈溢出)
存活时间:这里的内存和方法相关的;调用一个方法就会创建栈帧;方法执行结束;栈帧销毁
栈是私有的吗?
变量捕获:也能访问;所以说栈是私有的是不严谨。应该说每个线程有自己私有的栈;而不是是栈是私有的;
堆区
堆:整个JVM空间最大区域;new出来的对象都在堆上;类的成员变量也都是在堆上。一个进程只有一份;所有线程共用一个
一个我们上面的原始图就是一个进程;每一个java进程就是上面的这样一个JVM
两个进程为什么不共用一个JVM?
只能说很难办;那就别办了。操作系统办好了;但是不好办的;而java进程恰好是隔离性做好的;直接用现成的就好了;这样子不必处理就能有隔离性;让他们不会互相影响。
元数据区(以前叫方法区)
元:meta;属性。所以类对象这在这里;注意是类对象;是static?不限于static(方法的字节码和运行时数据都存在方法区)
类对象:常量池(放在元数据区好处;编译器确定;不再修改;让多个对象共享就有利于节省空间)、静态成员
字符串之所以在常量池的好处:这是一种特殊类型;编译器就确定;而且保持不变;并且可以被多个对象共享;所以放在这里有利于节省空间
方法区也是一个线程只有一块;然后多个线程共用;
public final :可能被编译器优化成字面值常量(就在常量池里);也可能没有优化;看你有没有加static。
JVM类加载机制
类加载过程
类加载:.class文件从硬盘加载到内存(元数据区;能在程序运行时快速访问和使用类的这些信息)过程
加载:找到这个.class文件的过程;打开文件;读文件内容
验证:检查.class文件的格式是否符要求;官方规范文档上详细描述.class格式
准备:给类对象分配内存空间(相当于先占个位置;内存初始化全0)
解析:针对字符串常量进行初始化;把符号转为引用
初始化:针对类对象的内容进行初始化内存空间;还有加载、执行静态代码块代码
解析这里把符号转为引用是什么意思呢?
字符串常量,得有一块内存空间,存这个字符的实际内容还得有一个引用,来保存这个内存空间的起始地址
在类加载之前,字符串常量,此时是处在 .class 文件中的此时这个“引用”记录的并非是字符串常量的真正的地址;可以认为是个占位符。类加载后才把字符串常量放到内存中;这时候才有真正的内存地址;这个引用才能被真正赋值到正确的内存地址
双亲委派模型:描述加载找.class的基本过程
三个类加载器:
1:BootstrapClassLoader负责加载标准库中的类((java 规范要求提供哪些类;无论是哪种JVM 的实现都会提供这些一样的类)
2:ExtensionClassLoader 负责加载JVM 扩展库中的类(不同JVM实现的厂商会做不同扩展一些额外功能)
3:ApplicationClassLoader 负责加载用户提供的第三方库/用户项目代码中的类
它们存在一个父子关系;爷爷、爸爸、孙子。不是继承那个父子关系;而相当于每一个class loader有一个parent属性指向自己的父类加载器
类似递归逻辑:
首先加载从 ApplicationClassLoader 开始;是 ApplicationClassLoader 会把加载任务交给父亲;而 ExtensionClassLoader 要去加载了;他又委托给自己的父亲BootstrapClassLoader 要去加载了。BootstrapClassLoader 也想委托自己的父亲结果发现,自己的父亲是 null;没有父亲/父亲加载完了,没找着类,才由自己进行加载。
父类没找到就让子类去找;如果到ApplicationClassLoader还没找到就抛出类找不到的异常
为什么要设置这个查找顺序:
1:假设用户自己写一个java.lang.String类;按这个加载顺序不会说加载时候会让你的类代替人家标准库的类
2:用户自定义的类加载器;也可以加入上述流程;跟一个类似链表一样;把自己写的类加载器挂到它们上面;这样子类加载可能扩展了
破坏双亲委派模型:
我们不一定要遵循双亲模型;自己写的类加载器也可以不遵守;看需求是什么;比如Tomcat加载webapp是单独类加载器;不遵守双亲委派模型文章来源:https://www.toymoban.com/news/detail-658404.html
什么时候触发类加载呢?
一个类只有真正被用到才加载(懒汉模式);但是加载过后;后续就不必重复加载;那么什么时候才算被用到呢?
例如:创建对象、调用静态方法等
你要调用静态属性;当然是要依赖于类对象;new对象跟建房子一样;你得要有图纸文章来源地址https://www.toymoban.com/news/detail-658404.html
到了这里,关于JVM内存区域划分的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!