JVM基础

1、JVM位置

在这里插入图片描述

2、JVM体系

在这里插入图片描述

3、类加载器 由上至下

作用:加载class文件- new Student(); 引用放在栈区,具体实例在堆区

BootstrapClassLoader(启动类加载器)->

ExtClassLoader (标准扩展类加载器)->

AppClassLoader(系统类加载器)->

CustomClassLoader(用户自定义类加载器)

java自带的类加载器:
启动类加载器(Bootstrap ClassLoader):又名根类加载器或引导类加载器,负责加载%JAVA_HOME%\bin目录下的所有jar包,或者是-Xbootclasspath参数指定的路径,例:rt.jar

拓展类加载器(Extension ClassLoader):负责加载%JAVA_HOME%\bin\ext目录下的所有jar包,或者是java.ext.dirs参数指定的路径

系统类加载器(Application ClassLoader):又名应用类加载器,负责加载用户类路径上所指定的类库,如果应用程序中没有自定义加载器,那么次加载器就为默认加载器

4、双亲委派机制

在类的加载时,不会由当前加载器直接加载,而是先委托父加载器,父加载器在委托父加载器的父加载器,直到最上层的父加载器时,再尝试加载,如果加载失败,再由子加载器加载。
好处举例:自定义的java.lang.String不会替代jdk的String

避免重复加载 + 避免核心类篡改

1.类加载器收到类加载的请求!

2.将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器

3.启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否者就抛出异常

4.重复步骤3

类加载流程(三个阶段):
1.加载阶段

将编译好的class文件加载到内存中(方法区),然后会生成一个代表这个类的Class对象。
2.链接阶段

会为静态变量分配内存并设置默认值。
3.初始化阶段

执行类构造器()进行初始化赋值。

5、沙箱安全机制

Java安全模型的核心就是Java沙箱(sandbox)

什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏

沙箱主要限制系统资源访问,那系统资源包括什么?

——CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。所有的Java程序运行都可以指定沙箱,可以定制安全策略。在Java中将执行程序分成本地代码和远程代码两种,本地代码默认视为可信任的,而远程代码则被看作是不受信的。对于授信的本地代码,可以访问一切本地资源。而对于非授信的远程代码在早期的Java实现中,安全依赖于沙箱Sandbox)机制。

组成沙箱的基本组件:

1.字节码校验器(bytecode verifier) :确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。

2.类裝载器(class loader) :其中类装载器在3个方面对Java沙箱起作用

它防止恶意代码去干涉善意的代码;

它守护了被信任的类库边界;

它将代码归入保护域,确定了代码可以进行哪些操作。

在这里插入图片描述

6、native

程序中使用:private native void start0();

1.凡是带了native关键字的,说明java的作用范围达不到了,回去调用底层c语言的库!
2.会进入本地方法栈,然后去调用本地方法接口将native方法引入执行

本地方法栈(Native Method Stack)

内存区域中专门开辟了一块标记区域: Native Method Stack,负责登记native方法,在执行引擎( Execution Engine )执行的时候通过本地方法接口(JNI)加载本地方法库中的方法

本地方法接口(JNI)

本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序, Java在诞生的时候是C/C++横行的时候,想要立足,必须有调用C、C++的程序,然后在内存区域中专门开辟了一块标记区域: Native Method Stack,负责登记native方法,在执行引擎( Execution Engine )执行的时候通过本地方法接口(JNI)加载本地方法库中的方法

7、PC寄存器(计数器)

程序计数器: Program Counter Register

每个线程有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用于存储指向一条指针的地址,也即将要执行的指令代码),在执行引擎读取下一个指令,是一个很小的内存空间,几乎可以忽略不计。

为什么需要程序计数器?记录要执行的代码位置,防止线程切换重新执行
字节码执行引擎修改程序计数器的值

8、方法区(Method Area)

方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间。

静态变量(static)、常量(final)、类信息(构造方法、接口定义)(Class)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关

9、栈:数据结构

程序=数据结构+算法

栈:先进后出,后进先出

栈:栈内存,主管程序的运行,生命周期和线程同步,线程结束,栈内存也就释放,低于栈来说不存在垃圾回收问题。

栈:8大基本类型+对象引用+实例的方法

栈运行原理:栈帧 栈满了:StackOverflowError

每个线程都有自己的栈,栈中的数据都是以栈帧的格式存在;在这个线程上正在执行的每一个方法都各自对应一个栈帧;栈帧是一个内存区块,是一个数据集维系着方法执行过程中的各种数据信息

栈+堆 +方法区:交互关系

在这里插入图片描述
队列:先进先出(FIFO)

为什么main方法先执行最后结束:先执行main()方法,被压入栈底,然后等调用其他方法结束后,main方法才会从栈里结束。

10、堆

Heap ,一个jvm只有一个堆内存,堆内存大小可以调节

类加载器读取类文件后,一般会把什么东西放在堆中?

类,方法,常量,变量,保存我们所有引用类型的真实对象;

堆内存中还要细分为3个区域:

  • 新生区: 类诞生和成长和死亡是地方

  • 伊甸园区:所有的对象都在伊甸园区new出来的

  • 幸存者区(0,1):轻GC之后存下来的

老年区(养老区):多次轻GC存活下来的对象放在老年区

永生区: 这个区域常驻内存的,用来存放jdk自带的class对象,interface元数据,存储的是java运行时的一些环境或类信息,该区域不存在垃圾回收,关闭VM虚拟就会释放这个区域的内存。

什么时候出现永久区满?

一个启动类,加载了大量的第三方jar包。tomcat部署了太多的应用,大量动态生成的反射类,不断地被加载,直到内存满就会出现OOM。

  • jdk1.6之前:永久代,常量池在方法区

  • jdk1.7 :永久代慢慢退化,去永久代,常量池在堆中

  • jdk1.8:无永久代,常量池在元空间(永生区1.8之后叫元空间)

    元空间:逻辑上存在,物理上不存在

在这里插入图片描述

默认情况下,JVM使用的最大内存为电脑总内存的四分之一,JVM使用的初始化内存为电脑总内存的六十四分之一.

总结:

  • 栈:基本类型的变量,对象的引用变量,实例对象的方法
  • 堆:存放由new创建的对象和数组
  • 方法区:Class对象,static变量,常量池(常量)

在这里插入图片描述
垃圾回收分为轻量级垃圾回收与重量级垃圾回收

GC垃圾回收主要是 伊甸园区与养老区。

假设内存满了,OOM,堆内存不够!

java.lang.OutOfMemoryError:java heap space

1、内存溢出:(Out Of Memory—-OOM)
系统已经不能再分配出你所需要的空间,比如系统现在只有1G的空间,但是你偏偏要2个G空间,这就叫内存溢出
例子:一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出。

比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出。说白了就是我承受不了那么多,那就报错。

2、内存泄漏: (Memory Leak)
强引用所指向的对象不会被回收,可能导致内存泄漏,虚拟机宁愿抛出OOM也不会去回收他指向的对象,意思就是你用资源的时候为他开辟了一段空间,当你用完时忘记释放资源了,这时内存还被占用着,一次没关系,但是内存泄漏次数多了就会导致内存溢出

软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。

弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。

虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收的活动。

项目中出现oom故障如何排除

  • 尝试扩大堆内存看结果

  • 分析内存,看一下哪个地方出现问题(专业工具)

  • 能够看代码第几行出错,内存快照分析工具,MAT, Jprofiler。

  • Dubug ,一行行分析代码

MAT, Jprofiler作用:

分析Dump内存文件,快速定位内存泄漏;

获得堆中的数据;

获得大的对象;

在这里插入图片描述

Jprofiler:idea 安装插件+官网安装客户端 使用。

下载地址:[ Jprofiler下载]ej-technologies - Java APM, Java Profiler, Java Installer Builder

添加参数运行程序:
-Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError:当出现OOM错误,会生成一个dump文件(进程的内存镜像)

常见JVM调优参数:

配置参数功能
-Xms初始堆大小。如:-Xms256m
-Xmx最大堆大小。如:-Xmx512m
-Xmn新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%
-XX:NewRatio新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3
-XX:SurvivorRatio新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10
-XX:+PrintGCDetails打印 GC 信息
XX:+HeapDumpOnOutOfMemoryError让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分析用

大致使用步骤:

1.在项目目录下找到dump文件,双击打开

在这里插入图片描述

2.可以看到什么占用了大量的内存,这里可以看到哪一行代码出现问题

在这里插入图片描述

11、GC

GC:垃圾回收机制

作用区域:方法区与堆区

GC两种类:轻GC,重GC
在这里插入图片描述

**1、引用计数法:**原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为 0 的对象。此算法最致命的是无法处理循环引用的问题。

在这里插入图片描述

2、复制算法:

此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理。

  • 好处:没有内存的碎片

  • 坏处 :浪费了内存空间(to区永远是空的)

    复制算法最佳使用场景:对象存货活较低的时候;新生区

在这里插入图片描述在这里插入图片描述
3、标记清除算法

此算法执行分两阶段。第一阶段从引用根节点开始标记可回收对象,第二阶段遍历整个堆,把未标记的对象清除。

  • 缺点:两次扫描严重浪费时间,需要暂停整个应用,会产生内存碎片。

  • 优点:不需要额外空间,不会浪费内存空间

在这里插入图片描述
4、标记清除压缩算法

此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有可回收对象,第二阶段遍历整个堆,清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。
此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题

在这里插入图片描述

GC分代收集算法

年轻代:

  • 存活率底
  • 复制算法

老年代:

  • 区域大,存活率高
  • 标记清除(内存碎片不是太多)+标记压缩混合实现

轻GC与重GC执行前提:

1.绝大多数刚刚被创建的对象会存放在Eden区
2.当Eden区第一次满的时候,会触发MinorGC(轻GC)。首先将Eden区的垃圾对象回收清除,并将存活的对象复制到S0,此时S1是空的。
3.下一次Eden区满时,再执行一次垃圾回收,此次会将Eden和S0区中所有垃圾对象清除,并将存活对象复制到S1,此时S0变为空。
4.如此反复在S0和S1之间切换几次(默认15次)之后,还存活的对象将他们转移到老年代中。
5.当老年代满了时会触发FullGC(全GC)

MinorGC

  • 使用的算法是复制算法
  • 年轻代堆空间紧张时会被触发
  • 相对于全收集而言,收集间隔较短

FullGC

  • 使用的算法一般是标记压缩算法
  • 当老年代堆空间满了,会触发全收集操作
  • 可以使用 System.gc()方法来显式的启动全收集
  • 全收集非常耗时

总结

内存效率:复制算法>标记清除算法>标记压缩算法

内存整齐度:复制算法=标记压缩算法>标记清除算法

内存利用率:标记压缩算法=标记清除算法>复制算法

12、JVM调优

对JVM内存的系统级的调优主要的目的是减少GC的频率和Full GC的次数。

JVM性能调优方法和步骤:
1.监控GC的状态;
2.生成堆的dump文件;
3.分析dump文件;
4.分析结果,判断是否需要优化;
5.调整GC类型和内存分配;
6.不断的分析和调整

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值