java虚拟机(Jvm )理解与问题

JVM相关知识

JVM主要包括四个部分:类加载器,内存区域,执行引擎,本地库接口
1)类加载器:在JVM启动时或者在类运行时将需要的class加载到JVM中

2)内存区(运行时数据区):是在JVM运行的时候操作所分配的内存区
运行时内存区主要可以划分5个区域:

①方法区【Method Area】:用于存储类结构信息的地方,包括常量池、静态变量、构造函数等

②堆【heap】:堆主要用来存放所有new出来的对象。存储java实例或者对象的地方

③栈【stack】:主要是用来执行程序的。java栈总是和线程关联在一起,每当创建一个线程时,JVM就会为这个线程创建一个对应的java栈。在这个java栈中又会包含多个栈帧,每运行一个方法就创建一个栈帧,用于存储局部变量表、操作栈、方法返回值等。每一个方法从调用直至执行完成的过程,就对应一个栈帧在java栈中入栈到出栈的过程。所以java栈是现成私有的。
存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)

④程序计数器【PC Register】:用于保存当前线程执行的内存地址

⑤本地方法栈【Native Method Stack】:和java栈的作用差不多,只不过是为JVM使用到的native方法服务的。

类与类加载器,双亲机制,Android类加载器

当你写完了一个.java文件的时候,编译器会把他编译成一个由字节码组成的class文件,当程序运行时,JVM会首先寻找包含有main()方法的类,把这个class文件中的字节码数据读入进来,转化成JVM中运行时对应的Class对象。执行这个动作的,就叫类加载器。
JVM不是一开始就把所有的类都加载进内存中,而是只有第一次遇到某个需要运行的类时才会加载,且只加载一次。

对于任何一个类,都需要由加载它的类加载器和这个类来确立其在JVM中的唯一性。也就是说,两个类来源于同一个Class文 件,并且被同一个类加载器加载,这两个类才相等。

双亲委托机制:
1.先检查需要加载的类是否已经被加载,如果没有被加载,则委托父加载器加载,父类继续检查,尝试请父类加载,这个过程是从下——-> 上;

2.如果走到顶层发现类没有被加载过,那么会从顶层开始往下逐层尝试加载,这个过程是从上 ——> 下;

类的加载过程

Person p = new Person();

//这其实是包含了两个步骤,声明和实例化

Person p = null; //声明一个名为Person类的对象p 声明:创建类的对象的过程 (栈内存 p)

p = new Person(); // 实例化这个p对象 实例化:使用关键字new 开辟堆内存空间

将堆内存内容的地址存在栈内存中

1)先找到person.class文件,并加载到内存中
2)执行该类中的static代码块,如果有的话,给person.class类进行初始化
3)在堆内存中开辟空间分配内存地址
4)在堆内存中建立对象的特有属性,并进行默认初始化
5)对属性进行显示初始化
6)对对象进行构造代码块初始化
7)对对象进行与之对应的构造函数进行初始化
8)将堆内存地址赋给栈内存中的p变量

Java引用类型(强引用,软引用,弱引用,虚引用)

1、强引用
代码中普遍存在的类似”Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

2、软引用
描述有些还有用但并非必需的对象。在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。Java中的类SoftReference表示软引用。

3、弱引用
描述非必需对象。被弱引用关联的对象只能生存到下一次垃圾回收之前,垃圾收集器工作之后,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。Java中的类WeakReference表示弱引用。

4、虚引用
这个引用存在的唯一目的就是在这个对象被收集器回收时收到一个系统通知,被虚引用关联的对象,和其生存时间完全没关系。Java中的类PhantomReference表示虚引用。

Java垃圾回收(GC)机制

Java垃圾回收(GC)机制详解
1. 为什么需要垃圾回收
如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收。除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此。所以,垃圾回收是必须的。
2. 哪些内存需要回收
所谓“要回收的垃圾”无非就是那些不可能再被任何途径使用的对象。
3. 垃圾回收器负责:
分配内存
保证所有正在被引用的对象还存在于内存中
回收执行代码已经不再引用的对象所占的内存
这里写图片描述
1.年轻代(Young Generation)
○ Java应用在分配Java对象时,这些对象会被分配到年轻代堆空间中去
○ 这个空间大多是小对象并且会被频繁回收
○ 由于年轻代堆空间的垃圾回收会很频繁,因此其垃圾回收算法会更加重视回收效率

2.年老代(Old Generationn)
○ 年轻代堆空间的长期存活对象会转移到(也许是永久性转移)年老代堆空间
○ 这个堆空间通常比年轻代的堆空间大,并且其空间增长速度较缓
○ 由于大部分JVM堆空间都分配给了年老代,因此其垃圾回收算法需要更节省空间,此算法需要能够处理低垃圾密度的堆空间

3.持久代(Permanent Generation)
○ 存放VM和Java类的元数据(metadata),以及interned字符串和类的静态变量

OOM是怎么出现的,有哪几块JVM区域会产生OOM,如何解决

内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

memory leak会最终会导致out of memory!

JVM的内存区域大致可以分为Java堆、方法区、虚拟机栈、本地方法栈、程序计数器。除了程序计数器不可能发生OOM之外,其他区域都可能发生OOM。

Java堆:Java堆发生内存溢出,主要是活着的对象占用的空间超过堆空间的大小。避免的方法可以罗列如下:
1.分析是否创建的很多不必要创建的对象;
2.或者是已经应该死掉的对象,没有在正常的声明周期中死掉;
3.或者在前面都正常的情况下,考虑是否可以在成本可以接收的范围内对实现进行重构和优化,避免这种情况;
4.在前面都正常的情况下,查看堆、方法区等等占用内存的比例,看看是否可以调大堆的大小;
5.在前面都正常的情况下,查看物理内存和虚拟机内存的比例,考虑不影响机器上其他应用的前提下,适当加大虚拟机内存;
6.在前面都正常的情况下,考虑适当加大物理机内存。

方法区:方法区是存放Java类文件和运行时常量池的地方,导致OOM也应该主要是这两方面的原因。避免的方法罗列如下:
1.查看是否其他区域的信息是否放到方法区,如果是,考虑是否可以把他们迁出去;
2.分析运行时常量池是否占用内存过多,是否有无必要,就算有必要,是否可以用其他的方式代替,比如在堆中分配;
3.分析不需要的类文件是否过多,是否可以卸载不必要的类文件;
其他的优化方式,类似于Java堆的3.4.5.6,将Java对改成方法区即可。

虚拟机栈:虚拟机栈是Java执行方法的在内存中分配的空间,出现OOM可能有两种情况,栈深度溢出或者因为栈太多导致内存不够(这种情况主要出现在多线征程)。避免的方法罗列如下:
1.栈深度溢出的话,可以考虑加大Java虚拟机栈的大小,如果Java虚拟机栈的空间允许的话;
2.如果由于栈太多,导致OOM,可以考虑适当减小栈的深度(如果可以满足业务需求)来避免;
其他的解决方式可以考虑类似于Java堆的3.4.5.6,将Java对改成虚拟机栈即可。

本地方法栈:本地方法栈是Java虚拟机调用本地方法,所使用的栈空间。解决方法可以罗列如下:
1.考虑优化程序,减少调用本地方法调用的次数,或者加大调用次数减少内存即可;
2.加大物理内存;
3.在某些情况下,也可以考虑调小虚拟机内存,本地方法栈所占的大小。

猜你喜欢

转载自blog.csdn.net/luyuqin0115/article/details/80283214
今日推荐