1.虚拟机内存分区
Java虚拟机的内存区域分为堆、方法区、程序计数器、虚拟机栈、本地方法栈。其中堆和方法区是线程共享的,而栈和程序计数器是线程私有的。
- 程序计数器:是一块较小的内存空间,它可以看成是线程所执行的字节码的行号指示器 。解释器就是通过改变程序计数器来依次读取指令。
- 虚拟机栈:由一个一个的栈帧组成,一个栈帧描述的就是一个Java方法的内存模型,栈帧中包含局部变量表,操作数帧,动态链接,方法的返回地址等信息。每个方法的调用直至执行完毕的过程,就是对应着一个栈帧在虚拟机栈中的入栈出栈的过程。若JVM支持动态扩展虚拟机栈容量的话,当栈申请无法申请到足够内存就会抛出OutOfMemoryError。
- 本地方法栈:和虚拟机栈作用一样,不过本地方法栈是为Native方法服务,这是java设计初为能调用C语言而设计的。
- 堆:Java虚拟机中所管理内存中最大的一块。堆是所有线程共享的一块区域,几乎所有new出来的对象实例都在这里分配内存。
- 方法区:实在虚拟机完成累加载之后,存储这个类相关的类型信息,常,静态变量,即时编译器便后的代码缓存等数据。JDK8之前堆和方法区是连在一起的,HotSpot虚拟机给出的具体实现是永久代,JDK8之后将方法区的实现更换成了元空间,不再与堆连续,并且是存于本地内存,这意味着本地内存足够就不会发生OOM。
2.常用的GC方法
常见的GC算法主要有标记-清除、标记-复制和标记-整理三种:
- 标记-清除算法是在进行可达性分析之后,标记出所有需要回收的对象,并将其清除。标记-清除算法可能产生内存碎片,并且GC效率不稳定。
- 标记-复制算法将Java堆的新生代划分为一块Eden区和两块Survivor区。每次分配内存只使用Eden区和其中的一块Survivor区,发生GC时,将仍然存活的对象复制到另一块Survivor区。而如果剩余的Survivor空间不足以存放剩下的对象,剩余的对象就会通过分配担保机制进入老年代。标记-复制算法当中,如果Survivor区较大,就会造成比较严重的内存空间浪费;而如果Survivor区较小,就必须有额外的空间进行分配担保。
- 标记-整理算法和标记-清除算法相似,但是清除的过程不是直接清除内存,而是将仍然存活的对象移动到内存的一侧。
3. JVM类加载的过程
类加载的过程主要包括:加载、连接、初始化三个步骤。
- 加载的主要工作是类的二进制字节流(如.class文件)读取到内存中,生成class对象。非数组类型对象的加载是由类加载器来实现的,类加载器在协同工作时会遵循双亲委派模型。
- 连接过程主要可以划分为验证、准备、解析三个阶段。验证过程主要验证文件格式、元数据、字节码和符号引用;准备阶段在方法区为类变量分配内存并给变量赋零值;解析阶段则是将常量池中的符号引用替换为直接引用的过程。
- 初始化执行方法,根据使用者编写的Java代码来对类变量和其他资源进行初始化。
4. 双亲委派模型
系统中的ClassLoader在协同工作时会使用双亲委派模型。双亲委派模型是指,当类加载器需要加载类的时候,它会先把加载请求委派给父加载器,一直递归到顶层(即BootstrapClassLoader),当父类无法完成这个加载请求时,子类才会尝试进行加载。
双亲委派模型可以避免类被不同的类加载器重复加载,从而产生两个同名但内容不同的类;同时可以避免Java的核心API遭到篡改。 而如果需要打破双亲委派模型,可以重写loadClass()方法。
5.类加载种类
类加载器的作用是将类从.class文件加载到内存,主要分为三种:
- BootstrapClassLoader(启动类加载器):由C++实现,负责加载JAVA_HOME/lib目录下的jar包和类
- ExtensionClassLoader(扩展类加载器):负责加载JRE_HOME/lib/ext目录下的jar包和类
- AppClassLoader(应用程序类加载器):面向用户的类加载器。
本文详细介绍了Java虚拟机的内存区域,包括堆、方法区、虚拟机栈、本地方法栈和程序计数器,强调了线程共享与私有区域的区别。接着,讨论了常见的垃圾收集算法,如标记-清除、标记-复制和标记-整理,以及它们的优缺点。此外,文章还阐述了类加载的过程,包括加载、连接、初始化三个步骤,并讲解了双亲委派模型在类加载中的作用。最后,简要概述了类加载器的类型,如BootstrapClassLoader、ExtensionClassLoader和AppClassLoader。
1347

被折叠的 条评论
为什么被折叠?



