标签:Java GC 内存 JVM native 方法 加载
参考链接:https://www.bilibili.com/video/BV1iJ411d7jS?from=search&seid=12790643842994759263
目录
- 前言
- JVM的位置
- JVM的体系结构
- 类加载器
- 沙箱安全机制
- native方法区
- 程序计数器
- 方法区Method Area
- 深入了解栈
- 三种JVM
- 堆(重点)
- Jpofilter工具分析OOM原因
- 关于GC
- 浅谈JMM
前言
对JVM(Java Virtual Machine)的理解?
JVM的位置
JVM的体系结构
类加载器
- 作用:加载class文件~new Student()新建一个类后,其引用(即名字)是放在Java栈,其具体实例(即具体数据)是放在堆中
- 分类
- 虚拟机自带的加载器
- 启动类(根)加载器boot
- 扩展类加载器ext
- 应用程序加载器app
- 双亲委派机制(为了安全)
在上图中,是我们自己建一个lang包,而后在里面也定义了String类,接着在里面写main函数,但是程序却报错了(说是找不到main入口),这是为什么呢?
解答:由于双亲委派机制,因为在要执行程序时,需要classLoader(String class),而要找到哪个classLoder呢?由于双亲委派机制会一直向上找关于String类,app->ext->boot,由于找到boot中有String,则执行这里面的。而假如boot没有,就层层往下找直到找到。最后的null是因为上层是用c++写的,所以无法读到。 - 类加载器的总流程
- 类加载器收到类加载请求
- 将这个请求向上委托给父类加载器来完成,一直向上委托,直到启动类加载器
- 若该加载器不能加载(即没找到有这个class),则抛出异常,通知子加载器加载;否则,加载结束
- 重复步骤3
注意,都没找到就会报错:Class not found
大部分class都是在app加载器上加载的
图中最上面那个ExtClassLoader写错了,正确是BootstrapClassLoader
沙箱安全机制
https://blog.csdn.net/qq_30336433/article/details/83268945
native方法区
e.g., private native void hello();
- 凡是带了native关键字的,都说明java的作用范围是达不到了,需要去调用底层c的库
- 过程:进入本地方法栈Native Method Stack,然后调用本地方法接口(JNI,java native interface)。JNI的作用:扩展java的使用(因为java初期c、c++横行,所以java为了占据市场的策略),融合不同的语言为java所用。于是在内存区域专门开辟一块标记区域:Native Method Stack,登记native方法。在最终执行的时候,通过JNI加载本地方法库的方法。
- 在一般不常用,但在与硬件交互的时候可能需要用,如打印机,管理系统,而在企业级应用比较少用
程序计数器
(PC寄存器):Program Counter Register
每个线程有一个程序计数器,私有的。程序计数器是一个指针,指向方法区方法的字节码(即将要执行的指令代码),占据内存很小。
方法区
Method Area
方法区是所有线程所共享的,所有字段和方法字节码,以及一些特殊方法,如构造函数、接口代码也在此定义。简单的说,所有定义的方法信息都在这块。
静态变量、常量、类信息(构造函数、接口)、运行时的常量池都存放在方法区。但是实例变量存放在堆内存,与方法区无关。
下图是上图对应的JVM工作过程
深入了解栈
- 何为栈溢出StackOverFlowError
首先,栈是怎么使用的?在程序运行时,调用的程序就是将当前要运行的方法压入栈,而运行完就是把该方法弹出。例如:main(test();)
,即有个main函数,其调用test方法,在栈中是这样表示的:
而若是:main(test();); test(a();); a(test());
,此时就会出现:
- 关于栈内存:
- 主管程序运行(程序正在执行的方法是在栈顶),与线程的生命周期一样
- 当线程完成后,栈也就释放掉了,所以不存在垃圾回收问题
- 栈中存放的东西:八大基本类型+对象引用+实例方法
- 栈帧:即栈中每个方法(即每一块)的对应的具体结构
- 栈+堆+方法区的交互关系
三种JVM
堆(重点)
- 一个JVM只有一个堆内存,堆内存的大小是可以调节的
- 类加载器加载类后会把什么东西放在堆中?类,方法,变量,常量~也就是引用类型的具体实例
- 有3个区域
- 新生区(伊甸园) young/new/eden
- 养老区 old
- 永久存储区 perm
注意:上图是jdk1.8以前的,而在jdk1.8后“永久存储区”改为“元空间”!
- GC(Garbage Collection)垃圾回收
GC主要是作用在伊甸园区和养老区
堆的内存溢出,即OOM(OutOfMemory)。例如String a="aaaa"; while(true){a+=new random.nextInt(bound:999999);}
。该内存溢出的过程:一次次地在伊甸园区满,然后到幸存区,然后到养老区... - 细讲三个区
伊甸园满了->轻Minor gc->幸存区0(1)->新生区都满->重full gc->养老区->再满->OOM
其中,元空间实际在本机内存,而不是堆中
Jpofilter工具分析OOM原因
关于GC
- 位置:堆和方法区
伊甸园、幸存区(from,to)、老年区(主要GC位置在伊甸园) - 类型
轻GC(普通PC)、重GC(全局GC)
- 算法
- 引用计数法
每个对象安排一个计数器,记录用的次数,而后次数少的会被淘汰
用的比较少,不高效 - 复制算法(作用在新生区)
幸存区from和to的概念是动态的:我们规定to的区得是空的,所以若现在to区有东西,from区没东西,那from区就变成to区;
好处:不会有内存碎片,不会东一块西一块
坏处:浪费内存空间,多了一半空的空间
所以,该算法最佳使用场景是对象存活率较低 - 标记清除算法
两次扫描:第一次对存活对象标记,第二次清除未存活对象。弊端是需要两次扫描以及需要成本来标记,会产生内存碎片。好处是不需要像方法2浪费一半的幸存区,即不需要额外空间。
优化:标记压缩算法
在两次扫描的基础上,添加压缩步骤,但是又多了一次扫描成本:
继续优化:多次清除(前两次)+一次压缩(最后一次) - GC算法总结
浅谈JMM
JMM(Java Memory Model)
标签:Java,GC,内存,JVM,native,方法,加载 来源: https://www.cnblogs.com/Cindy-H/p/13669355.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。