• 一. JVM发展史,运行时数据区域,四大引用


    一.JVM的出现

    1. JVM将字节码解释成不同os下的机器指令,有了jvm,java语言在不同平台上运行时不需要重新编译

    2. 虚拟机发展史
      (1)Sun Classic

      1. classic jvm要么采用纯解释器解释执行,要么采用JIT编译执行,一旦JIT进行编译执行,则解释器不再生效
      2. 如果使用JIT编译代码,则JIT会对每个方法,每行代码都进行编译,对于那种只需运行一次,不具有编译价值的代码,也会被JIT编译执行。迫于程序响应时间的压力,此阶段的JIT不敢采用编译耗时的优化技术,所以及时采用JIT输出本地代码,他的执行效率也和C代码有很大差距。被人诟病“java语言很慢”

      (2)Exact VM

      1. Exact VM进入了高性能虚拟机时代,开始使用编译器解释器混合模式
      2. Exact VM使用了精确式内存管理,他能判定内存中摸个位置的数据具体是什么类型。eg:内存中有一个数是12345,ExactVM可以判断他是一个int数字,还是一个指向内存12345地址的引用。这种精确式内存管理,在gc时,可以直接回收引用类型的数据,少了像classic vm时代gc时的数据类型查找过程

      (3)HotSpot虚拟机

      1. Hot Spot是当前jdk采用的虚拟机 (1.4以后)
      2. 热点代码探测技术:通过计数器找出最具编译价值的代码,通知JIT以方法为单位进行编译。如果方法被频繁调用,则触发标准编译;如果方法中循环次数很多,触发栈上替换编译动作。
      3. HotSpot无需等待本地代码输出后才能执行程序,使得即时编译压力减小,有助于采用更更多的代码优化技术。输出高质量的操作系统本地代码

    二. JVM运行时数据区域

    1. 线程隔离的数据区
      (1)程序计数器:

      1. 字节码的行号指示器。解释器根据程序计数器选取要执行下条指令。分支,循环,跳转,异常处理,线程恢复等基础功能都需要程序计数器来完成。
      2. 为了线程切换后能继续执行正确的代码,每条线程都要维护自己的程序计数器,各个线程间的程序计数器互不影响
      3. 如果线程执行的是native方法,则该线程的程序计数器值为空

      (2)虚拟机栈:

      1. 虚拟机栈用来执行java方法
      2. 每个java方法执行的时候都会创建一个栈帧。栈帧用来存储局部变量表,操作数栈,动态链接,方法出口等信息。

      (3)本地方法栈

      1. 本地方法栈用来执行native方法
    2. 线程共享数据区
      (1)java堆

      1. java堆存储对象实例
      2. java堆分为新生代和老年代。新生代分为eden,from survive,to survive空间

      (2)静态区

      1. 静态区也叫做永久带,存放类信息,常量池,静态变量,JIT编译后的代码等。
      2. 常量池技术:为了方便创建一个对象二出现的。当需要创建一个对象时,如果常量池中有该对象,则直接从池子中取出来,在重复创建相等变量时节省了很多时间。eg:str1=“abc”,str2=“abc”。这样直接声明的字符串会放到常量池中,所以str1和str2是静态区常量池中的同一块内存地址

    三. JVM的4中引用

    1. 强引用:new对象。其他引用方式都实在强引用失效(置成null后才有用)

    2. 软引用
      (1)一个对象被强引用new出来以后,在jvm中,可以存在软引用
      (2)对象=null使得强引用失效后,如果对象还存在软引用,则引用的对象在gc时不会被垃圾回收。只有当jvm发生内存溢出前,软引用引用的对象才被回收。因此,软引用可以被当做缓存

      Person p = new Person();
      SoftReference<Person> softReference = new SoftReference<>(p);
      
      p = null;   // 断开强引用
      System.gc();
      System.out.println(softReference.get().getName());  // 输出aaa
      
    3. 弱引用
      触发gc时,就会被当做垃圾回收。若引用的生命周期是强引用失效后,到gc触发时

          Person p = new Person();
          WeakReference<Person> reference = new WeakReference<>(p);
      
          p = null;   // 断开强引用
          System.out.println(reference.get().getName());  // 输出aaa
          System.gc();
          System.out.println(reference.get().getName());  // 抛出nullexception
      
    4. 幽灵引用
      幽灵引用和没有引用一样,对象的生命周期和只有强引用时一样。只是在对象被回收时会收到一个系统通知

          Person p = new Person();
          ReferenceQueue<Person> referenceQueue = new ReferenceQueue<>();
          PhantomReference<Person> reference = new PhantomReference<>(p,referenceQueue);
      
          p = null;   // 断开强引用
          try{
              System.out.println(reference.get().getName());  // 抛出nullpointer Exception
          }catch (Exception e){e.printStackTrace();}
          System.gc();
          try{
              System.out.println(reference.get().getName());  // 抛出nullpointer Exception
          }catch (Exception e){e.printStackTrace();}
      

    【注】:软引用和若引用都延长了对象引用的生命周期,一个延长至oom发生,一个延长至gc发生时。幽灵引用只是提供对象gc的通知
    ThreadLocal使每个线程保存各自的变量副本,值得注意的是,这个变量要在每个线程内部初始化,因为ThreadLocal不会生成变量副本,如果在线程外初始化变量,仍然有线程安全问题

  • 相关阅读:
    jmeter非GUI模式运行单节点
    Feign详细构建过程及自定义扩展
    【springboot 源码解析】springboot 依赖管理
    通知神器——java调用钉钉群自定义机器人
    【乱纪】Markdown中常用的乱七八糟的东西
    【Java入地】 01 多线程与高并发
    【Linux】默认文本编辑器 vim 的入门与进阶
    【Linux】 安装搜狗输入法
    【Linux】系统发展史上的血雨腥风
    【Java进阶】01 Lambda 使用与进阶
  • 原文地址:https://www.cnblogs.com/72808ljup/p/5348475.html
Copyright © 2020-2023  润新知