JVM和GC讲解及面试题

JVM

因为内存管理交给java虚拟机处理,不需要创建和释放堆,所以不容易出现内存泄漏和内存溢出问题
在Java编译器和OS平台之间的虚拟处理器。可以在上面运行Java字节码程序。
也就是说我们写好的代码,通过编译器编译成.class(字节码)文件后,就在JVM上运行。

java内存区域

JDK8 的内存区域
在这里插入图片描述

线程共享:堆,方法区,直接内存
线程私有:程序计数器,栈(他俩生命周期都和线程相同)

程序计数器

一块较小的内存空间,它是当前线程所执行的字节码的行号指示器,我们熟悉的分支操作、循环操作、跳转、异常处理和线程恢复等基础模型都需要依赖这个计数器来完成。
因此每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。

Java 虚拟机栈

该区域也是线程私有的,它的生命周期也与线程相同。包含局部变量表,方法返回地址指向对象的引用

本地方法栈

功能基本与虚拟机栈相同,一般与虚拟机栈合称为栈

Java中的堆是用来存储对象本身的以及数组(因为数组也是对象)。 被所有线程共享,也是垃圾回收的区域。
Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆(Garbage Collected Heap)从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代。对象首先被分配到新生代,当进行每次垃圾回收时,如果对象年龄+1,长期存活到对象会被放到老年代

元空间(方法区)

之前版本叫永久代,是方法区的实现。jdk8以后改为元空间,放在直接内存中。存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量常量以及编译器编译后的代码等。
java垃圾回收也是要在方法区进行的,他是在满了之后,出发完全垃圾回收机制。因为他是共享变量空间。

java GC 垃圾回收机制

对于C和C++来说堆内存都是开发人员分配和释放。而java通过GC机制来对内存进行维护和释放。而不容易造成内存泄漏和内存溢出问题。
**内存泄漏:**是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。
内存溢出: 给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。

JVM将堆分为两大区,新生代和老年代,方法区又叫永久代
当空间不够的时候就进行回收。清理频率很高。

1. Java垃圾判定算法

  1. 引用计数法:每当又一个对象被其他引用指向就增加1。当没有的时候减为0。就可以被回收。
  2. 可达性算法分析:JVM通过该算法来判定对象是否存活。如GC-root的对象作为起始点然后向下搜索,当一个对象到GCroot没有任何引用链时,也就是通过GCroot到一个对象不可达时,则证明这个对象是不可用的。
    在这里插入图片描述

2. JVM在什么时候进行回收

  1. cpu空闲时
  2. 调用System.gc()尝试回收
  3. 堆内存满时

3. (如何回收)java垃圾收集算法

四种:标记-清除算法,复制算法,标记-整理算法,分代收集算法.

1. 标记-清除

该算法标记阶段是标记出所有需要被回收的对象,然后清除被标记的。但是很大碎片化问题。

2. 标记整理算法

也是标记要回收的对象,然后不直接清理,而是把存活的都移动到一端,然后清理,解决碎片化问题。

3. 复制算法

内存分为两块。每次使用一块。一块满了之后,就把存活的复制到另一块,然后清除第一块。
解决碎片化,空间利用率低。

4. 分代收集算法(复制+标记清除)

当前JVM采用的算法。
将java堆分成新生代和老年代比例为1:2

在新生代中:每次都有大批对象死去,少量存活,所以使用复制算法,只复制少量存活对象,内存分为两块,一块存储,一块空闲,当存储用完了之后就将存活的对象复制到另一块然后清空该块。避免内存碎片。如果对象经过几次回收后仍存活则放到老年代。
在老年代中:使用标记清除算法。或者标记整理。
在这里插入图片描述

面试题

JVM调优参数(什么是-xms参数和-xmx参数)

-XMS为JVM初始堆内存大小,默认为物理内存的1/64
-XMX为JVM最大分配堆内存大小,默认为物理内存堆1/4
我们可以调整这两个参数来扩展程序使用堆内存的大小。

-XSS是设置线程栈内存大小默认512k-1024k
-XX:metaspacesize:元空间大小
-XX:NewSize: 新生代大小
-XX : NewRatio 新生代和老年代占比

强,软,弱,虚引用

强引用:就是我们普通创建对象的引用,GC无论如何都不会回收。因为他表明对象还活着。
因为JVM不会回收,所以他是导致JVM内存泄漏的主要原因之一。
软引用:内存不够的情况下,GC才回收。
虚、弱引用:虚引用和弱引用必定被回收

OOM(out of memory)

原因

  1. 分配等内存太少
  2. 应用用的太多,没释放。造成内存泄漏或内存溢出

内存泄漏

使用完对象没有及时释放,还占用内存中空间。
比如我们在做网络连接或者数据库连接的时候,建立连接后要关闭。如果连接过多而没有关闭,GC不会回收,造成大量对象没有被回收,而引起内存泄漏。
一次内存泄漏并不影响,但是累计多了就造成内存溢出。

内存溢出

简单来说就是内存不能够满足我们程序需要运行的内存了。就是内存不足。

举例:不停向一个list中存数据,造成堆内存不够用

  public static void main(String[] args) {
        List<OOB> list = new ArrayList<OOB>();
        while (true) {
            list.add(new OOB());
        }

最常见的情况。根据提示信息修改参数。比如栈内存,元数据内存,堆内存溢出。
可以通过修改堆内存参数,永久带内存参数,和栈内存参数解决。

当一个线程内存溢出,其他线程能正常运行么

正常来说是不能的,因为多线程共享内存,堆内存溢出说明整个程序都要崩溃。但是实际上,一个线程的内存溢出,抛出异常,他就会立即释放占用资源,而保证其他线程正常运行。

发布了103 篇原创文章 · 获赞 94 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/chongbin007/article/details/102531611