JVM学习

Java不是最强大的语言,但是JVM是最强大的虚拟机

虚拟机

Virtual Machine:

  1. 含义:就是一台虚拟的计算机,它是一款软件,用来执行一系列虚拟计算机指令;
  2. 分类:系统虚拟机和程序虚拟机
    1. 系统虚拟机:完全是对无力计算机的仿真;
    2. 程序虚拟机:专门执行单个计算机程序而设计;

Java虚拟机

Java Virtual Machine

  1. 跨语言(scale,grovery),跨平台;
  2. 作用:就是二进制字节码的运行环境
  3. 特点:
    1. 一次编译,到处运行;
    2. 自动内存管理;
    3. 自动垃圾回收功能;
  4. JVM是运行在操作系统上,没有与硬件直接交互;
  5. JDK -> JRE -> JVM
  6. Hotspot Vm
  7. 虚拟机是目前市面上高性能虚拟机的代表作之一;

Java代码执行流程

  1. Java程序–编译–>字节码文件
  2. Java源码–生成–>Java编译器
  3. todo:流程图

jvm 架构模型

Java编译器输入的指令流基本上是一种基于栈的指令集架构,另外一种指令集架构则是基于寄存器的指令集架构;

  1. 基于栈式架构的特点:
    1. 设计和实现更简单,适用于资源受限的系统;
    2. 避开寄存器的分配难题:使用零地址指令方式分配;
    3. 指令流中的指令大部分都是零地址指令,其执行过程依赖于操作栈,指令集更小,编译器更容易实现;
    4. 不需要硬件支持,可移植性更好,更容易实现跨平台;
  2. 基于寄存器的指令集架构:
    1. 完全依靠硬件,可以移植性查;
    2. 性能优秀和执行效率更高;

内存结构

  1. 运行时数据区:
  2. 执行引擎
  3. 垃圾回收

类加载

  1. 类加载子系统:
    1. 负责从文件系统或者网络中加载Class文件,class文件在文件开头要求有特定的标识;
    2. 加载的类信息存放于方法区;除了类的信息外,方法区还存放运行时常量信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)
    3. 类装载器ClassLoader
  2. 类加载过程
    1. 加载Loading
      1. 通过一个类的全限定名获取定义此类的二进制字节流
    2. 链接(Linking)
      1. 验证(Verify)
        有效标识:CA FE BA BE
        目的:为了保证文件的字节流符合当前虚拟机要求;
        验证方式:文件格式验证(),元数据验证,字节码验证,符号引用验证;
      2. 准备(Prepare)
        1. 为初始化变量赋默认值
        2. 不包含final 修饰的static,因为final在编译的时候就分配了,准备阶段会显示初始化;
        3. 这里不会为实例变量初始化;类变量会分配在方法区中,而实例变量会随着对象一起分配到Java堆中
        c. 解析(resolve)
      3. 初始化(init)
        执行类构造器方法()的过程;
        此方法是javac编译器自动生成的;
        ()不同于类构造器;
        一个内部类至少存在一个类的构造器;
        虚拟机必须保证一个类的()方法在多线程下能被同步加载;
    3. 类加载器的分类
      1. JVM支持两个类型的加载器:
        引导类加载器(Bootstrap ClassLoader):C和C++编写
        自定义加载器(UserDefine ClassLoader):
      2. 虚拟机自带的加载器:
        启动类加载器(BootStrap ClassLoader):C 和C++编写
        扩展类加载器Extention ClassLoader:Java语言编写,派生于CLassLoader类;
      3. 应用程序类加载器(系统类加载器,APPClassLoader):该类加载是程序中默认的类加载器
    4. 用户自定义加载器:
      1. 为什么使用?
        1. 隔离加载类
        2. 修改类加载的方式;
        3. 修改加载源;
        4. 防止源码泄露;
      2. 如何自定义加载类:
        1. 继承抽象类java.lang.ClassLoader
        2. jdk1.2之后,建议把自定义的逻辑卸载findClass()中;
          除启动类加载器之外,其他启动类都继承自抽象类ClassLoader;
    5. 加载,加载之后进行验证,验证是否为.class文件,验证之后进行准备,准备之后进行数据解析,解析之后初始化,初始化之后开始使用,使用完成之后卸载;加载:类加载器加载.class文件;加载过程最终的产物是在堆的class对象;

双亲委派机制

工作原理:
    1. 如果一个类加载器收到了一个类加载请求,他并不会自己先去加载,二十把这个请求委托给父类的加载器去执行;
    2. 如果父类加载器还存在其谷类加载器,则进一步向上委托,依次地柜,请求最终将到达顶部的启动类加载器
    3. 如果父类可以完成类加载器任务,就成功返回,如果父类加载器无法完成此任务,子类加载器才会尝试加载;
为什么使用:

区域划分:

  1. 线程共享区:
    1. 堆:存放对象实例和数组 --> jvm优化技术,逃逸分析(-XX:+DoEscapeAnalysis)
    2. 方法区:
  2. 线程私有:
    1. 程序计数器,
    2. 虚拟机栈,
    3. 本地方法栈

引用

引用:

  1. 强引用:
    类似于Object object = new Object(); 强引用都不回被回收;
  2. 软引用:
    Softrefence类实现软引用;在系统要发生内存泄漏之前,将会把这些对象进回收范围之中进行二次回收;
  3. 弱引用:
    weakReference类实现;对象只能生存到下一次垃圾回收之前;无论内存是否够用都要回收;
  4. 虚引用:
    PhantomRefrence实现;无法通过一个虚引用获取一个实例;必须搭配ReferenceQueue使用,被标为虚引用的对象,在发生GC时会被放进队列,然后回收
引用类型被回收时间用途生存时间
强引用从来不会对象的一般状态JVM停止运行
软引用内存不足时对象缓存内存不足时
弱引用jvm垃圾回收对象缓存gc运行后
虚引用未知未知未知

记忆:
强 —— 有地位,不商量
弱 —— 无地位,直接收
软 —— 留情面,量满收
虚 —— 存队列,直接收

值传递和引用传递

1.值传递:你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
2.引用传递:你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。 但是,不管上面哪种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?

GC

  1. 如何判定对象是否为垃圾?

    1. 引用计数法(jvm一般不使用)

      1. 逻辑:
        1. 在对象中引用一个程序计数器
        2. 对象被引用,计数器+1
        3. 引用失效,计数-1
        4. 统计结果为0的对象作为结果
      2. 缺点:难以解决对象之间互相循环引用的问题;
      3. 应用:
        1. 微软COM计数
        2. Python
      4. 针对缺陷:
        1. 问题:如果A引用B,B引用A,那么这两个对象是否都会被回收;
        2. 关键不是在于A,B之间是否有引用,而是A,B是否可以一直向上追溯到GC Roots。如果与GC Roots没有关联,则会被回收,否则将继续存活。
    2. 可达性分析性算法(java使用)

      1. 逻辑:
        1. 通过GC Roots作为起点
        2. 通过起点向下遍历,走过的路径作为引用链;
        3. 仅判定引用链包含的对象作为可达对象;
      2. 哪些可以作为GC Roots
        1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
        2. 方法区中类静态属性引用的对象(一般指被static修饰的对象,加载类的时候就加载到内存中)
        3. 方法区中常量引用的对象
        4. 本地方法栈中JNI(即一般说的native方法)中引用的对象;
  2. 哪些区域需要回收?

    1. 无需GC
      1. 范围:程序计数器、虚拟机栈、本地方法栈
      2. 特点:随线程生,随线程死
      3. 原因:编译器即可知道内存占用大小,所以内存的回收和分配都具有确定性
    2. 需要GC
      1. 范围:虚拟机堆、静态区->常量池
      2. 原因:运行期动态创建,内存的分配和回收都有不确定性;
  3. 常用GC算法

    1. 标记-清除算法(mark-sweep)
      直接对垃圾对象进行标记,然后清除;
      缺陷:会产生很多的内存碎片;
    2. 标记-复制算法(mark-copy)
      将内存分为两半,总是保留一部分空着,将另一半存活的对象复制到另一半,然后左侧全部清除;
      缺陷:解决了内存碎片问题,但是内存浪费严重;
    3. 标记-压缩算法(mark-compoact)
      将垃圾对象清理后,将剩下的存活对象进行整理挪动,保证空间连续;
      缺陷:降低GC效率
    4. 分代收集法(generation-collect)
  4. 垃圾回收器

    1. Serial收集器
    2. ParNew收集器
    3. Parallel收集器
    4. CMS收集器(主流)
    5. G1收集器(主流)
  5. jvm 优化

    1. 堆设置
      1. -Xms 堆内存的初始大小,默认为物理内存的1/64
      2. -Xmx 堆内存的最大大小,默认为物理内存的1/4
      3. Xmn 堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn
      4. -Xss 设置每个线程可使用的内存大小,即栈的大小。
      5. -XX:NewRatio:设置新生代和老年代的比值。如:为3,表示年轻代与老年代比值为1:3
      6. -XX:SurvivorRatio:新生代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:为3,表示Eden:Survivor=3:2,一个Survivor区占整个新生代的1/5
      7. -XX:MaxTenuringThreshold:设置转入老年代的存活次数。如果是0,则直接跳过新生代进入老年代
      8. -XX:PermSize、-XX:MaxPermSize:分别设置永久代最小大小与最大大小(Java8以前)
      9. -XX:MetaspaceSize、-XX:MaxMetaspaceSize:分别设置元空间最小大小与最大大小(Java8以后)
    2. 收集器设置
      1. -XX:+UseSerialGC:设置串行收集器
      2. -XX:+UseParallelGC:设置并行收集器
      3. -XX:+UseParalledlOldGC:设置并行老年代收集器
      4. -XX:+UseConcMarkSweepGC:设置并发收集器
    3. 垃圾回收统计信息
      1. -XX:+PrintGC
      2. -XX:+PrintGCDetails
      3. -XX:+PrintGCTimeStamps
      4. -Xloggc:filename
    4. 并行收集器设置
      1. -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
      2. -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
      3. -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
    5. 并发收集器设置
      1. -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
      2. -XX:ParallelGCThreads=n:设置并发收集器新生代收集方式为并行收集时,使用的CPU数。并行收集线程数。

s

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值