JVM 意为 Java虚拟机
JVM的内存来自操作系统,内存主要划分为一下四个部分:
注意:每个线程都有一份栈和程序计数器,而整个进程有唯一的堆和方法区。加载类得到的类对象是在方法区中。同时static修饰的成员,是类属性(长在类对象身上),也是在方法区中。
代码在创建变量的时候,变量位于那个区域取决于代码,和变量的类型没有关系
堆的存储空间不够了,-Xms(设置堆的最小值),-Xmx(设置堆的最大值)
启动Java进程的时候,可以设置一些命令选项(JVM调参),这些选项会影响到JVM工作的具体特性
public class Test {static class OOMObject {}public static void main(String[] args) {List list =new ArrayList<>();while(true) {list.add(new OOMObject()); //这里不会触发CG(垃圾回收机制)}}
}
上述代码出现OOM异常,这里很可能是因为代码出现了”内存泄漏“,该释放的内存没有释放,Java的垃圾回收机制没有管住。
HotSpot虚拟机将虚拟机栈与本地方法栈合二为一
类加载就是将编译好的.class文件,给加载到内存中
类加载大致分为5步,其中有3步属于连接
加载只是类加载过程的一个阶段
校验.class文件格式是否符合预期(需要遵循Java虚拟机规范的要求),保证这些信息当作代码运行后不会危害虚拟机本身的安全
针对类对象进行一些初始化工作,初始化的同时,也需要针对静态变量进行设置值
Java虚拟机将常量池内的符号引用替换为直接引用的过程,也就是初始化常量的过程
Java虚拟机真正开始执行类中编写的Java程序代码,将主导权移交给应用程序。初始化阶段就是执行类构造器的过程。
双亲委派模型的工作过程是,如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去完成加载
假设要加载的类是一个java.lang.String(标准库的类)
假设要加载的类是一个自己写的类Java.test
如果最后的步骤仍然找不到这个类,就会抛出异常ClassNotFoundException
双亲委派模型是JVM里默认的方式,但是JVM也给我们提供了一些机制,可以让程序员自定义类加载器。自己定义的类加载器可以遵循双亲委派模型也可以不遵守,根据需求灵活控制。(eg:Tomcat)
一个进程的执行,是需要使用到计算机的硬件资源的,而计算机的硬件资源是有限的,尤其是对于内存来说。因此,不能光申请内存,也要释放内存。
内存申请的时候是明确的,而内存释放的时候是不一定明确的
不同语言缓解内存泄漏的方法
程序计数器
栈
前两个和线程绑定在一起,创建线程,这里的东西就被申请。线程结束(里面的run方法执行完),相关的内存就可以销毁
堆(引用存放在堆中
CG主要负责这里,堆上面创建的内存属于整个进程都可以用到的内存,具体啥时候用到就比较模糊
方法区
方法去里主要是类对象,类加载的时候,申请这里的内存。类卸载很少涉及
Java堆中存放着几乎所有对象的实例,垃圾回收器在对堆进行垃圾回收前,首先要判断这些对象那些存活,那些已经死去。将死去的对象进行回收
判定对象是否是垃圾,判断对象是否有引用指向它
Java没有使用,Python等语言使用
JVM没有选用引用计数法来管理内存,最主要的原因就是引用计数法无法解决对象的循环引用问题
JVM中的可达性分析,就是从代码中显式书写出的这些引用变量出发,遍历每一个可以访问的路径(类似于二叉树的遍历,递归进行)把所有可以访问到的对象都标记为可达,而不可达的就是垃圾!接下来JVM就可以将未标记可达的对象清除掉。
可达性分析解决了引用计数中的问题
标记-清除分别表示两个不同的阶段,首先标记需要回收的对象,然后对标记的对象进行统一的回收。
不足之处:
最大优点:充分解决内存碎片问题
标记(可达性分析),这里不直接清除内存碎片,将保留的对象复制到另一半。保证释放后的空间有足够大的空闲空间
缺点:
标记-清除的改进版本,删除类似于顺序表删除中间元素,带着一个搬运的操作
JVM中不是只使用了一种算法,而是多种算法结合起来一起使用
当前 JVM 垃圾收集都采用的是"分代收集(Generational Collection)"算法,这个算法并没有新思想,只 是根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代。在新生代中,每 次垃圾回收都有大批对象死去,只有少量存活,因此我们采用复制算法;而老年代中对象存活率高、没 有额外空间对它进行分配担保,就必须采用"标记-清理"或者"标记-整理"算法。
哪些对象会进入新生代?哪些对象会进入老年代?
注意:以上都是抽象出来的简化模型,真实的垃圾回收会更加复杂
针对垃圾回收算法的具体实现
《Java虚拟机规范》中对垃圾收集器应该如何实现并没有做出任何规定,因此不同的厂商、不同版本的虚拟机所包含的垃圾收集器都可能会有很大差别,不同的虚拟机一般也都会提供各种参数供用户根据自己的应用特点和要求组合出各个内存分代所使用的收集器。