Java虚拟机(JVM)知识整理

JVM是Java Virtual Machine(Java虚拟机)的缩写 . 用于运行 Java 编译后的二进制字节码,最后生成机器指令

JVM的基本结构

在这里插入图片描述

方法区(Method Area)被所有线程共享,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
运行时常量池(Runtime Constant Pool)是方法区的一部分。保存 Class 文件中的符号引用、翻译出来的直接引用。运行时常量池可以在运行期间将新的常量放入池中。

Java 堆(Java Heap)是 Java 虚拟机中内存最大的一块。Java 堆在虚拟机启动时创建,被所有线程共享。存放对象实例

Java 虚拟机栈(Java Virtual Machine Stacks)是线程私有的,生命周期与线程相同。
虚拟机栈描述的是 Java 方法执行的内存模型:每个方法被执行的时候都会创建一个栈帧(Stack Frame)存有 1局部变量表 2操作栈 3动态链接 4方法出口
在这里插入图片描述
本地方法栈(Native Method Stacks)为虚拟机使用到的 Native 方法服务。

程序计数器(Program Counter Register)是一块较小的内存空间,可以看作是当前线程所执行字节码的行号指示器。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。

String str = new String("hello");

上面的语句中变量str放在上,用new创建出来的字符串对象放在上,而"hello"这个字面量是放在方法区的。

补充1:较新版本的Java(从Java 6的某个更新开始)中,由于JIT编译器的发展和"逃逸分析"技术的逐渐成熟,栈上分配、标量替换等优化技术使得对象一定分配在堆上这件事情已经变得不那么绝对了。
补充2:运行时常量池相当于Class文件常量池具有动态性,Java语言并不要求常量一定只有编译期间才能产生,运行期间也可以将新的常量放入池中,String类的intern()方法就是这样的。

类加载器

在这里插入图片描述

  • 启动类加载器:(Bootstrap ClassLoader):使用C++实现(仅限于HotSpot),是虚拟机自身的一部分。负责将存放在JAVA_HOME\lib目录中的类库 rt.jar加载到虚拟机中。其无法被Java程序直接引用。

  • 扩展类加载器:(Extention ClassLoader)由ExtClassLoader实现,负责加载JAVA_HOME\lib\ext类库,开发者可以直接使用。

  • 应用程序类加载器:(Application ClassLoader):由APPClassLoader实现。负责加载用户类路径(ClassPath)上所指定的类库

类加载机制 (双亲委派模型)

类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载

优点 : 避免类的重复加载,另外也避免了java的核心API被篡改。

“双亲委派”机制只是Java推荐的机制,并不是强制的机制。

我们可以继承java.lang.ClassLoader类,实现自己的类加载器。如果想保持双亲委派模型,就应该重写findClass(name)方法;如果想破坏双亲委派模型,可以重写loadClass(name) 方法

分类: JVM

为何有打破双亲委派模型

因为在某些情况下父类加载器需要委托子类加载器去加载class文件。受到加载范围的限制,父类加载器无法加载到需要的文件
例子 JNDI代码由启动类加载器去加载,但它需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者(SPI, Service Provider Interface)的代码,但启动类加载器不可能“认识“这些代码啊。因为这些类不在rt.jar中,但是启动类加载器又需要加载.为了解决这个问题 设计了 线程上下文类加载器(Thread Context ClassLoader) 这个类加载器可以通过java.lang.Thread类的setContextClassLoader方法进行设置。如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过多的话,那这个类加载器默认即使应用程序类加载器。

嘿嘿,有了线程上下文加载器,JNDI服务使用这个线程上下文加载器去加载所需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载的动作,这种行为实际上就是打通了双亲委派模型的层次结构来逆向使用类加载器,实际上已经违背了双亲委派模型的一般性原则。但这无可奈何,Java中所有涉及SPI的加载动作基本胜都采用这种方式。例如JNDI,JDBC,JCE,JAXB,JBI等。

JVM 垃圾回收机制
  1. Java 的4种引用方式

强引用 Strong Reference

软引用 Soft Reference
用来描述一些还有用,但并非必须的对象。软引用所关联的对象,有在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围,并进行第二次回收。如果这次回收还是没有足够的内存,才会抛出内存异常。提供了SoftReference类实现软引用。

弱引用 Weak Reference
描述非必须的对象,强度比软引用更弱一些,被弱引用关联的对象,只能生存到下一次垃圾收集发生前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。提供了 WeakReference 类来实现弱引用。

虚引用 Phantom Reference
一个对象是否有虚引用,完全不会对其生存时间够成影响,也无法通过虚引用来取得一个对象实例。为一个对象关联虚引用的唯一目的,就是希望在这个对象被收集器回收时,收到一个系统通知。提供了 PhantomReference 类来实现虚引用。

  1. 内存分配与回收策略
    在这里插入图片描述
    JVM 中共划分为三个代:年轻代、老年代和持久代。
    年轻代:存放所有新生代的对象;
    老年代:在年轻代经历了N次垃圾回收仍然存活的对象,将被放到老年代中,故都是一些生命周期较长的对象;
    持久代:用于存放静态文件,如java类、方法等。
    新生代的垃圾收集器名为"minor gc",老生代的GC命名为"Full Gc 或者 Major GC",其中System.gc()强制执行的Full Gc

  2. 有哪些垃圾收集算法?
    如何定义jvm中的内存垃圾。1.引用计数法,2.可达性分析算法
    A. 标记-清除算法
    B. 复制算法(新生代,当回收时,将 Eden 和 Survivor 中还存活的对象一次性拷贝到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。Hotspot 虚拟机默认 Eden 和 Survivor 的大小比例是8:1,也就是每次新生代中可用内存空间为整个新生代容量的90%(80% + 10%),只有10%的内存是会被“浪费”的。)
    C. 标记-整理算法(老年代, 让所有存活的对象向一端移动,然后直接清理掉边界以外的内存。)
    D. 分代收集算法(一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点,采用最适当的收集算法。)

    垃圾回收器,CMS和G1

  3. jvm调优
    常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。

    1. jconsole,Java Monitoring and Management Console是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控
    2. jvisualvm,jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。
    3. MAT,Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
    4. GChisto,一款专业分析gc日志的工具
常见面试基础问题
  • Java字节码文件的格式
  • 内部类的存储方式
  • 垃圾回收器的分类及优缺点
  • 类在虚拟机中的加载过程
  • 即时编译器的前后端优化方法
  • CMS垃圾回收器的工作过程
  • CAS指令以及其他线程安全的方法
  • 各种内存溢出的情况,包括JNI调用
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值