jvm-常见问题
来源:http://www.tudoupe.com时间:2022-02-09
谈一下对jvm的理解
- jvm是存在于操作系统上(Linux,Windows,Mac)的
- java程序是跑在jvm上的
- jvm是用c语言实现的
- 安装JRE即包含安装了jvm环境,如果只是跑程序不需要安装jdk
- jvm一共有三种:
- sun公司的Hotspot,可通过java -version查看当前jvm版本
- IBM的J9VM JIT编译器
- BEA的 JRockit
jvm的体系结构

JVM模型 - class loader:类加载器
- 根据《Java 虚拟机规范》中的说法,Java 虚拟机的内存结构可以分为【公有】和【私有】两部分。
私有指的是每个线程的私有数据,包括:PC寄存器、Java 虚拟机栈、本地方法栈。
公有指的是所有线程都共享的部分,指的是Java 堆、方法区、常量池
Class Loader类加载器【重要!】
类加载器工作流程

- 类加载器作用
- 加载编译后的class文件
- 加载后生成【可实例化对象实例的class模版类对象】
- 根据类模板对象【实例化对象】(new 对象)
- class模版类对象 与 普通类对象区别:
- 一个class文件对应一个class模板类对象,即不管Car.class执行多少个carClass,这些carClass都是同一个class模板对象
- java类加载器的类型
- Bootstrap Loader:启动类加载器,也叫根加载器,其可加载到的类对应jre环境中rt目录下的jar包
- ExtClassLoader:扩展类加载器,其可加载到的类对应jre环境中EXC目录下的jar包
- AppClassLoader:应用程序加载器,即开发人员自定义的类,这些类会生成对应的应用程序加载类,比如Student加载类
- 类加载器的加载机制
- 双亲委派机制:
- jvm接收到加载class文件的请求
- 先使用【启动类加载器】加载该class文件,如果需要加载的类的全包名在rt下存在,则使用启动类加载器加载rt下的类
- 再使用【扩展类加载器加载】
- 最后【使用应用程序加载器】
- 比如:新建一个 java.lang.String类,但是该类与【启动类加载器】中的String类重包且重名,则此时使用String类时,就会使用rt下的String类
- 沙箱安全机制
- 目的:防止恶意代码被执行
- 双亲委派机制:
- native关键字
- 凡是带了native关键字的方法,说明这java的作用范围达不到了,会去调用底层C语言的库;
- 凡是带了native关键字的,会进入native本地方法栈,然后调用本地方法接口【JNI-Java native interface】
- JNI作用:扩展Java的使用,融合不同的编程语言为Java所用,比如C、C++
- 程序结束器【线程独有】
- 每个线程都有一个程序计数器,作用是记住当前线程执行的位置。
- 方法区【全局共享】
- 方法区可以看成是一个接口,对于方法区的实现,不同虚拟机中策略也不同。以我们常用的HotSpot虚拟机为例,其设计团队使用永久带来实现方法区,并把GC的分代收集扩展至永久带
- 方法区中保存:

JDK1.6 
JDK1.7 
JDK1.8
栈【线程独有】
- 栈:首先是一种数据结构。
- 栈里的数据是先入后出,可以将栈看成一个桶,先进的被压在了下层,后进的在上层。
- 为什么main方法先执行,但总是最后结束?
- 先将main方法放到栈中
- 如果在main方法中调用A方法,于是将A方法也放入栈中,压在main方法上
- 如果在A方法中也调用B方法,同理将B方法也放入栈中,压在A方法上
- 当B方法执行完成后,将B方法弹出栈
- 接着继续执行A方法,当A方法执行结束时,将A方法弹出栈
- 接着继续执行main方法,当main方法执行结束时,将main方法也弹出栈,至此,main方法执行结束
- 栈的生命周期和线程同步,线程结束,占内存也被释放了。固栈中不存在垃圾回收问题
- 栈里面存储哪些东西
- 属于八大基本类型的局部变量的值(String、类等属于引用类型,固栈中不存放String)
- 属于引用类型的局部变量的引用 (该引用指向的对象分配在了堆中)
- 实例的方法
- 1和2的理解:因为声明方法时,相当于往栈中放了一个栈帧,固该方法中声明的局部变量被分配与栈中
- 如果栈中线程的深度 > 栈允许的总深度:StackOverFlowError【属于错误,错误会使程序停掉】
堆(Heap)
- 堆:先进先出,FIFO~>first into first out
- 一个jvm只有一个堆
- 堆内存大小是可以调节的
- 类加载器加载了class文件后,会向堆中存放哪些东西呢
- 几乎所有的对象实例对象和数组都在堆中分配
- 属于八大基本类型的成员变量,其引用和值均存在堆中的类对象里面(String、类等属于引用类型,固栈中不存放String)
- 属于引用类型的成员变量,其引用和对象 均存在堆中的类对象里面
- 综合2和3来说,不管成员变量是基本类型还是引用类型,其引用、值或者是对象均存在与堆中
- jdk1.7以后,还保存了字符串常量池和静态变量
- 垃圾回收主要就是作用于这里的
- 堆中备份为三个区域

- 堆中垃圾回收机制:Minor GC和Major GC区别
- Minor GC:简单理解就是发生在年轻代的GC。三步(复制--清空--互换)
- Major GC又称为Full GC。当年老代空间不够用的时候,虚拟机会使用“标记—清除”或者“标记—整理”算法清理出连续的内存空间,分配对象使用。
- 新生代【Young】
- 新生区占对空间的1/3,包含:伊甸园区【Eden Space】、Survivor From区、Survivor To区,其比例为8:1:1
- 伊甸园区:Java大部分新创建的对象都会被分配在伊甸园区,如果对象太大则会直接分配到老年区,当伊甸园区的内存不够时,会触发Minor GC复制算法的GC回收对象
- 新生区的处理机制:
- 第一次Yong GC(Minor GC)后,Eden区还存活的对象复制到Surviver区的“To”区,“From”区还存活的对象也复制到“To”区,
- 再清空Eden区和From区,这样就等于“From”区完全是空的了,而“To”区也不会有内存碎片产生,
- 等到第二次Yong GC时,“From”区和“To”区角色互换,很好的解决了内存碎片的问题。
- 对象在Survivor区中每“熬过”一次GC,年龄就会+1。待到年龄到达一定岁数(默认是15岁),虚拟机就会将对象移动到年老代
- 老年代【Old】
- 主要存放应用程序中生命周期长的内存对象
- 老年代的对象比较稳定,所以MajorGC不会频繁执行
- MajorGC采用标记清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC 会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。当老年代也满了装不下的时候,就会抛出OOM (Out of Memory)异常
- 永久区【Perm,JDK1.7以前】
- 内存的永久保存区域,主要存放Class 和Meta (元数据)的信息
- GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class 的增多而胀满,最终抛出OOM异常。
- 元空间【jdk1.8及以后】
- 在jdk1.8以后,jvm使用元空间替代了永久区,固1.8的jvm模型如下:

- 区别:元空间的内存使用的本地的物理内存,不再使用jvm的堆内存
- 元空间保存:类信息、运行时常量池
- 在jdk1.8以后,jvm使用元空间替代了永久区,固1.8的jvm模型如下:
上一篇:Linux-v10-01天-授课
下一篇:没有了
相关新闻
- 2022-02-09 Linux-v10-01天-授课
- 2022-02-08 使用Anaconda创建python环境 & 常见
- 2022-02-08 职业健康检查报告七大常见问题探
- 2022-02-08 cmder使用简介
- 2022-02-08 组件分享之后端组件——Nginx中流媒
- 2022-02-08 高效解决虚拟机无法联网的问题
- 2022-02-08 编写优雅代码的7个不太为人所知的
- 2022-02-08 windows dos命令常见
- 2022-02-08 DEBUG:plt保存失败
- 2022-02-08 不同编程语言之间的具体实现对比
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
