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

JVM相关知识

DVM指dalivk的虚拟机。每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例。而每一个DVM都是在Linux 中的一个进程,所以说可以认为是同一个概念。

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字符串和类的静态变量

什么情况下GC会执行

1.当程序中调用System.gc()方法触发。这个方法应避免出现在程序中调用。因为JVM有足够的能力来控制垃圾回收。
2.当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外
3.Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止
由于是否进行主GC由JVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的。

如何判断一个对象是否存话(GC对象判定的方法)

垃圾收集器对 Java堆里的对象 是否进行回收的判断准则:Java对象是存活 or 死亡
在Java虚拟机中,判断对象是否存活有2种方法:

  1. 引用计数法
  2. 可达性分析法

引用计数法

给 Java 对象添加一个引用计数器。每当有一个地方引用它时,计数器 +1;引用失效则 -1;
无法解决 对象间相互循环引用 的问题:两个对象相互进行引用,除此之外这两个人对象没有任何引用。实际上这两个对象已经不可能再被访问,应该要被垃圾收集器进行回收。 但因为他们相互引用,所以导致计数器不为0,这导致引用计数算法无法通知垃圾收集器回收该两个对象

正由于该算法存在判断逻辑漏洞,所以 Java虚拟机没有采用该算法判断Java是否存活

可达性分析法

常说的GC(Garbage Collector) roots,特指的是垃圾收集器(Garbage Collector)的对象,GC会收集那些不是GC roots且没有被GC roots引用的对象。

  1. 可达性分析
  2. 第一次标记 & 筛选
  3. 第二次标记 & 筛选

可达性分析

将一系列的 GC Roots 对象作为起点,从这些起点开始向下搜索= 引用链
当一个对象到 GC Roots 没有任何引用链相连时,则判断该对象不可达
在这里插入图片描述

哪些对象可以作为GC Roots?

虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI(即一般说的native方法)中引用的对象

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.在某些情况下,也可以考虑调小虚拟机内存,本地方法栈所占的大小。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值