微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Java之JVM篇

参考链接https://www.bilibili.com/video/BV1iJ411d7jS?from=search&seid=12790643842994759263

目录

前言

对JVM(Java Virtual Machine)的理解?

JVM的位置

JVM的体系结构

类加载器

  • 作用:加载class文件~new Student()新建一个类后,其引用(即名字)是放在Java栈,其具体实例(即具体数据)是放在堆中


  • 分类

  1. 虚拟机自带的加载器
  2. 启动类(根)加载器boot
  3. 扩展类加载器ext
  4. 应用程序加载器app
  • 双亲委派机制(为了安全)


    在上图中,是我们自己建一个lang包,而后在里面也定义了String类,接着在里面写main函数,但是程序却报错了(说是找不到main入口),这是为什么呢?
    解答:由于双亲委派机制,因为在要执行程序时,需要classLoader(String class),而要找到哪个classLoder呢?由于双亲委派机制会一直向上找关于String类,app->ext->boot,由于找到boot中有String,则执行这里面的。而假如boot没有,就层层往下找直到找到。最后的null是因为上层是用c++写的,所以无法读到。
  • 类加载器的总流程
  1. 类加载器收到类加载请求
  2. 将这个请求向上委托给父类加载器来完成,一直向上委托,直到启动类加载器
  3. 若该加载器不能加载(即没找到有这个class),则抛出异常,通知子加载器加载;否则,加载结束
  4. 重复步骤3
    注意,都没找到就会报错:Class not found
    大部分class都是在app加载器上加载的


    图中最上面那个ExtClassLoader写错了,正确是BootstrapClassLoader

沙箱安全机制

https://blog.csdn.net/qq_30336433/article/details/83268945

native方法

e.g., private native void hello();

  1. 凡是带了native关键字的,都说明java的作用范围是达不到了,需要去调用底层c的库
  2. 过程:进入本地方法Native Method Stack,然后调用本地方法接口(JNI,java native interface)。JNI的作用:扩展java的使用(因为java初期c、c++横行,所以java为了占据市场的策略),融合不同的语言为java所用。于是在内存区域专门开辟一块标记区域:Native Method Stack,登记native方法。在最终执行的时候,通过JNI加载本地方法库的方法
  3. 在一般不常用,但在与硬件交互的时候可能需要用,如打印机,管理系统,而在企业级应用比较少用

程序计数器

(PC寄存器):Program Counter Register
每个线程有一个程序计数器,私有的。程序计数器是一个指针,指向方法方法的字节码(即将要执行的指令代码),占据内存很小。

方法

Method Area
方法区是所有线程所共享的,所有字段和方法字节码,以及一些特殊方法,如构造函数、接口代码也在此定义。简单的说,所有定义的方法信息都在这块。
静态变量、常量、类信息(构造函数、接口)、运行时的常量池都存放在方法区。但是实例变量存放在堆内存,与方法区无关。



下图是上图对应的JVM工作过程

深入了解栈

  1. 何为栈溢出StackOverFlowError
    首先,栈是怎么使用的?在程序运行时,调用的程序就是将当前要运行的方法压入栈,而运行完就是把该方法弹出。例如:main(test();),即有个main函数,其调用test方法,在栈中是这样表示的:


    而若是:main(test();); test(a();); a(test());,此时就会出现:

  2. 关于栈内存:
  • 主管程序运行(程序正在执行的方法是在栈顶),与线程的生命周期一样
  • 当线程完成后,栈也就释放掉了,所以不存在垃圾回收问题
  • 栈中存放的东西:八大基本类型+对象引用+实例方法
  1. 栈帧:即栈中每个方法(即每一块)的对应的具体结构

  2. 栈+堆+方法区的交互关系

三种JVM

堆(重点)

  1. 一个JVM只有一个堆内存,堆内存的大小是可以调节的
  2. 类加载器加载类后会把什么东西放在堆中?类,方法,变量,常量~也就是引用类型的具体实例
  3. 有3个区域
  • 新生区(伊甸园) young/new/eden
  • 养老区 old
  • 永久存储区 perm


    注意:上图是jdk1.8以前的,而在jdk1.8后“永久存储区”改为“元空间”!
  1. GC(Garbage Collection)垃圾回收
    GC主要是作用在伊甸园区和养老区
    堆的内存溢出,即OOM(OutOfMemory)。例如String a="aaaa"; while(true){a+=new random.nextInt(bound:999999);}。该内存溢出的过程:一次次地在伊甸园区满,然后到幸存区,然后到养老区...
  2. 细讲三个区
    伊甸园满了->轻Minor gc->幸存区0(1)->新生区都满->重full gc->养老区->再满->OOM



    其中,元空间实际在本机内存,而不是堆中

Jpofilter工具分析OOM原因

关于GC

  • 位置:堆和方法
    伊甸园、幸存区(from,to)、老年区(主要GC位置在伊甸园)
  • 类型
    轻GC(普通PC)、重GC(全局GC)

  • 算法
  1. 引用计数法
    每个对象安排一个计数器,记录用的次数,而后次数少的会被淘汰


    用的比较少,不高效
  2. 复制算法(作用在新生区)


    幸存区from和to的概念是动态的:我们规定to的区得是空的,所以若现在to区有东西,from区没东西,那from区就变成to区;


    好处:不会有内存碎片,不会东一块西一块
    坏处:浪费内存空间,多了一半空的空间
    所以,该算法最佳使用场景是对象存活率较低
  3. 标记清除算法
    两次扫描:第一次对存活对象标记,第二次清除未存活对象。弊端是需要两次扫描以及需要成本来标记,会产生内存碎片。好处是不需要像方法2浪费一半的幸存区,即不需要额外空间。


    优化:标记压缩算法
    在两次扫描的基础上,添加压缩步骤,但是又多了一次扫描成本:


    继续优化:多次清除(前两次)+一次压缩(最后一次)
  4. GC算法总结

浅谈JMM

JMM(Java Memory Model)


版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐