初识JVM(一)

243 阅读5分钟

1、jvm 内存结构分区

jvm内存结构主要包含5个区域:java虚拟机栈本地方法栈程序计数器方法区

jvm.png

  • java虚拟机栈

每个线程的创建都会创建一个java虚拟机栈,每次调用方法又生成一个栈帧推入java虚拟机栈中。方法调对应的栈帧出栈。一旦所有栈帧都出栈了,该线程也就结束了。

栈帧包含4个部分:局部标量表、操作数栈、动态连接、完成出口

  • 本地方法栈

本地方法栈是一个和java虚拟机栈类似的区域,区别在于本地方法栈是用来管理本地方法调用的。

  • 程序计数器

java程序都多线程的程序,所以就会存在上下文的切换。那程序计数器就用来记录各个线程的执行信息的。

堆是线程共享的一个区域,主要用来存放:对象数组

新生代
  • eden区

    存放新new的对象和数组

  • survivor区

    存放经历过一次Minor GC多次Minor GC为晋级到老年代的对象或数组

老年代

存放通过相应机制晋级的对象数组

机制一:正常提升:通过15次Minor GC未被GC的对象或数组。可以通过参数: ‐XX:+MaxTenuringThreshold 来自定义。

机制二:动态对象年龄判:幸存区中相同年龄对象大小的和,大于幸存区的一半,大于或等于 age 的对象将会直接进入老年代。

机制三:大对象直接在老年代分配:默认0MB,可以通过参数:-XX:PretenureSizeThreshold 来自定义。

机制四:分配担保:新生代内存空间不足,又有新的对象需要分配空间。那该对象将直接晋级到老年代。

  • 方法区

方法区主要是用来存放:类信息常量池方法数据方法代码

类信息包含:类的版本、字段、方法、接口和父类信息等。tips:1.8 以后类信息是存在本地内存的(元空间)。

常量池包含:静态常量池、运行时常量池。tips: 常量池是在JVM堆内存里的。

总结:方法区是一个JVM标准,是一个逻辑概念。1.8以前是用永久代来进行实现的。1.8以后用元空间取代了永久代。

2、Java类加载机制

java类加载的生命周期主要包含7个阶段:加载、验证、准备、解析、初始化、使用、卸载。

jvm类加载机制.png

  • 加载

加载的主要过程是将外部的.class文件,加载到JVM方法区内。

对于我们的java项目无论是jarwar,并不会一上来就把包里的**.class**文件全部加载进去,而是会到需要时才会进行加载(如:new对象或反射生成对象)。

而为了避免重复的加载,加载时会遵循双亲委派原则。但是也有打破双亲委派原则的情况,如tomcat自定义的类加载器等。

  • 连接
  • 验证

验证阶段主要是为了验证加载进入方法区的.class文件是否符合当前虚拟机的标准,经常出现错误的情况有:用低版本的jvm跑一些高版本的java程序。

  • 准备

准备阶段主要是为类的静态变量分配内存,初始化为系统的初始化值。如 int 初始化值 0。

  • 解析

符号引用转化为直接引用符号引用:类和接口的全限定名、字段的名称和描述符、方法的名称和修饰符 直接引用:直接指向对应的内存位置。

  • 初始化

为类的静态变量赋真正初始值,执行静态代码块。如果有父类先初始化父类

  • 使用

  • 卸载

tips

new 一个对象的过程:判断对应的类是否已经加载,如果没有加载则进行上述的类加载过程。类加载完毕后创建对象,过程如下; 1、为对象在新生代分配大小合适的内存空间。

2、为实例变量赋默认值。

3、设置对应的对象头信息。

4、执行构造器。

3、双亲委派机制

双亲委派机制是为了jvm为了避免类的重复加载,类的加载安全。

  • 双亲委派大概流程

大概流程就是:每次类加载的时候都不自己加载,让父类进行加载,依次上推直至加载不了。

双亲委派流程.png

  • 类加载器
  • Bootstrap ClassLoader

    启动类加载器:主要任务加载核心类库,也就是rt.jar、resources.jar、charsets.jar 等。也可以指定jar包路径,

    参数为:-Xbootclasspath

    这个加载器是C++编写的,随JVM启动

  • Extention ClassLoader

    扩展类加载器:主要任务加载 lib/ext 目录下的 jar 包和 .class 文件。也可以通过系统变量 java.ext.dirs 可以指定这个目录。

    这个加载器是java类。

    ext classLoader.png

  • App ClassLoader

    应用程序类加载器: 主要加载我们自己写的java类,也就是classpath下的jar和class文件。

    App ClassLoader 也同样集成自 URLClassLoader

  • Custom ClassLoader

    自定义类加载器:支持个性扩展,如:tomcat打破双亲委派。

  • 打破双亲委派的案例
    • 案例一:Tomcat

    Tomcat发布war包应用,其实已经打破了双亲委派的原则。

    • 为什么要打破双亲委派原则

      在我们开发的war包应用中一般都会去依赖一些第三方的框架或jar包。并且我们通常会在一个tomcat中运行多种不同的war包应用。我们并不能保证这些war包所依赖的第三方框架和jar包都是同一个版本,那么就可能发生版本冲突。

    • 怎么打破双亲委派原则

      怎么为了解决这个问题,tomcat就定义了WebAppClassLoader来进行类加载,只有等待WebAppClassLoader没法加载,就会交给上层的 ClassLoader 进行加载。

    • 大概的Tomcat类加载器结构

    Tomcat类加载器.png

结尾

欢迎各位同学指点交流