JVM基础入门学习

9 篇文章 0 订阅

JVM学习

1. JVM的位置

img

2.JVM的体系结构

  1. image-20220709161501397

    !堆和方法区是所有线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的。

  2. image-20220709161633810

3.类加载器

加载class文件~

image-20220709161824110

  1. 引导类加载器(bootstrap class loader)

  2. 扩展类加载器(extensions class loader)

  3. 应用类加载器(application class loader)

  4. 自定义类加载器(custom class loader)

    引导类>扩展类>系统

4.双亲委派机制

百度,搜索就会有很多通俗易懂的介绍,也可以看下面一张图片理解一下

img

​ 根据上图中我们就可以容易理解了,当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。

还是不能理解可看这篇文章https://blog.csdn.net/codeyanbao/article/details/82875064

这个机制的好处就是消除安全隐患,防止危险代码植入!

5.Native关键字

native是java中的一个关键字, java语言是运行在虚拟机上的,java又是不允许直接访问硬件的(也就是java安全性的体现),而java想要做一些操作硬件的事情的话,必然要用到底层一些的调用。这就引出了native的关键字。这也是java的底层机制,实际上java就是在不同的平台上调用不同的native方法实现对操作系统的访问的。

**!补充:**使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。这些函数的实现体在DLL中,JDK的源代码中并不包含,你应该是看不到的。对于不同的平台它们也是不同的。

native关键字的实现原理

  1. native关键字作用在方法上,并且不提供实现体,它会进入本地方法栈,通过调用JNI(Java Native Interface)Java本地接口实现对其他语言代码和代码库的使用。
  2. 内存中有一块专门开辟的区域:Native Method Stack(本地方法栈),登记Native方法。
  3. JNI调用C流程图image-20220710110545136
  4. 可以将native方法比作Java程序同C程序的接口,其实现步骤:
    1. 在Java中声明native()方法,然后编译;
    2. 用javah产生一个.h文件;
    3. 写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件);
    4. 将第三步的.cpp文件编译成动态链接库文件;
    5. 在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。

更详细解析请看原文

6.栈、堆和方法区

    1. 虚拟机栈(JVM Stack)。虚拟机栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池的引用(Reference to runtime constant pool)、

      方法返回地址(Return Address)和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。

      先进后出,正在运行的方法永远都在栈顶!image-20220711144737240

    2. 本地方法栈(Native Stack)。本地方法栈与Java栈的作用和原理非常相似,区别是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。

  • Java中的堆是用来存储对象本身的以及数组(数组引用是存放在Java栈中的)。堆是被所有线程共享的,在JVM中只有一个堆

    所有通过new创建的对象的内存都在堆中分配,其大小可以通过**-Xmx-Xms**来控制。

    被划分为新生代和老年代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由FromSpace(s0)和ToSpace(s1)组成。结构图如下所示:

    下图中的Perm代表的是永久代,但是注意永久代并不属于堆内存中的一部分,同时jdk1.8之后永久代也将被移除,改名为元空间

    image-20220711160127963

  • 方法区

方法区(Method Area)也称”永久代“,与java堆一样,是各个线程共享的内存区域,它用于存储已经被虚拟机加载的类型信息,常量,静态变量,及时编译后的代码缓存等数据。方法区在JVM启动时候就被创建,并且它的实际物理内存空间和java堆区一样都是可以不连续的。

Java虚拟机规范中明确说明:“尽管所有的方法区在逻辑上是属于堆的一部分,但是一些简单的实现可能不会选择去进行垃圾回收或进行压缩”,但是对于HotSpot JVM 而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆区分开来。所以,方法区看作是一块独立于堆的内存空间。

方法区,元空间,永久代三者关系是什么呢?

方法区是java虚拟机规范的一部分,而元空间和永久代是一个具体的实现,元空间的本质和永久代类似,都是对JVM规范方法区的实现,不过两者最大的区别就是:元空间不在虚拟机中设置内存,而是直接使用本地内存.

在JDK8之前,方法区又称为"永久代",而JDK8以及8之后改为"元空间"image-20220711162618193

  • 栈,堆,方法区中的交互关系

image-20220711163427474

image-20220711163448659

方法区学习文章

扩展:PC寄存器----线程私有

PC寄存器也叫程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的信号指示器。
每一条JVM线程都有自己的PC寄存器。在任意时刻,一条JVM线程只会执行一个方法的代码,该方法称为该线程的当前方法(Current Method)。如果该方法是java方法,那PC寄存器保存JVM正在执行的字节码指令的地址;如果该方法是native,那PC寄存器的值是undefined。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

7.JVM调优

  1. 常见的JVM实现
    • Hotspot
      • oracle官方,我们做实验用的JVM
      • java -version
    • Jrockit
      • BEA,曾经号称全世界最快的JVM
      • 被oracle 收购,合并于Hotspot
    • J9-IBM
      • 市场定位与HotSpot接近,服务器端、桌面应用、嵌入式等多用途VM
      • 广泛用于IBM的各种Java产品,也称为Eclipse OpenJ9
    • Microsoft VM
    • TaoBaoVM
      • hotspot深度定制版
    • LiquidVM
      • 直接对硬件
  2. GC垃圾回收

前面的JVM体系结构讲解到,jvm调优99%都是在堆中(包括 ‘ 元空间 ’),因为在运行时数据区中,只有堆中存在着垃圾回收。所以学习调优就嘚深入学习堆的内部结构和知识,掌握常有调优参数和垃圾回收算法。

  • 堆的内部结构

    image-20220713215724062

    调优参数

    -Xms:初始分配大小,默认为物理内存的1/64

    -Xmx:最大分配内存,默认为物理内存的1/4

    -XX:+PrintGCDetails:输出详细的GC处理日志

    以IDEA为例,在需要运行的类的configuration里面的 VM options 里面。如下图:

    img

    • 常用参数配置
      • 收集器设置
        -XX:+UseSerialGC:设置串行收集器
        -XX:+UseParallelGC:设置并行收集器
        -XX:+UseParalledlOldGC:设置并行年老代收集器
        -XX:+UseConcMarkSweepGC:设置并发收集器
      • 垃圾回收统计信息
        -XX:+PrintGC
        -XX:+PrintGCDetails
        -XX:+PrintGCTimeStamps
        -Xloggc:filename
      • 并行收集器设置
        -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
        -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
        -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
      • 并发收集器设置
        -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
        -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
  • GC算法

    GC是分类收集算法,JVM在进行GC的时候并不是每次对三个区域一起回收,大部分时候是回收新生代。频繁收集Young区,较少收集Old区,基本不动元空间。GC按照回收的区域分成了:普通GC minor GC和全局GC Full GC。

    • minor GC

    复制 ☞ 清空 ☞ 互换

    1. eden、survivor from 复制到 survivor to,对象年龄+1。

    当eden区满,触发第一次GC,存活对象拷贝到survivor from区。当eden区再次触发GC,会扫描eden和from,对这两个区进行垃圾回收,将存活的对象,复制到to区,对象年龄+1。(如果有对象年龄达到了老年的标准,拷贝到老年代,对象年龄+1)

    1. 清空eden、survivor from

    清空eden和survivor from中对象,此时from为。

    1. survivor from 和 survivor to 互换

    to区存在对象,变成下一次GC的from区,from区成为下一次GC的to区,部分对象会在form和to区域复制往来15次(JVM的MaxTenuringshold参数默认是15),如果最终还是存活,就存入老年代。

    • Full GC

    Full GC是针对整个新生代、老生代、元空间(metaspace,java8以上版本取代perm gen)的全局范围的GC。触发Full GC的原因有很多:

    1. 当年轻代晋升到⽼年代的对象⼤⼩,并⽐⽬前⽼年代剩余的空间⼤⼩还要⼤时,会触发Full GC;
    2. 当⽼年代的空间使⽤率超过某阈值时,会触发Full GC;
    3. 当元空间不⾜时(JDK1.7永久代不足),也会触发Full GC;
    4. 当调⽤**System.gc()**也会安排⼀次Full GC。

    GC有四大算法

    1. 引用计数法

      引用计数法每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题,还有一个问题是如何解决精准计数。这种方法现在已经不用了 。

    2. 复制算法

      复制算法采用从根集合扫描,并将存活对象复制到一块新的,没有使用过的空间中,这种算法当控件存活的对象比较少时,极为高效,但是带来的成本是需要一块内存交换空间用于进行对象的移动。此算法用于新生代内存回收,从E区回收到S0或者S1

    3. 标记清除

      标记-清除算法采用从根集合进行扫描,对存活的对象标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收。标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片!适合老生代去回收。

    4. 标记压缩。

      标记-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时不同,在回收不存活的对象占用的空间后,会将所有的存活对象往左端空闲空间移动,并更新对应的指针。标记-整理算法是在标记清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。

  • JVM调优工具

    • Jconsole : jdk自带,功能简单,但是可以在系统有一定负荷的情况下使用。对垃圾回收算法有很详细的跟踪。
    • JProfiler:商业软件,需要付费。功能强大。
    • VisualVM:JDK自带,功能强大,与JProfiler类似。推荐

这些只是我个人参考多篇文章及视频的理解,有个别地方写得不好的理解一下,关于JVM调优的知识还有很多且很深,感兴趣的同学可以搜集一下JVM调优更深入的学习!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值