java 模块优化,模块二十三_jvm优化、linux优化

序言:

文章内容输出来源:拉勾教育Java高薪训练营。

本篇文章是学习课程中的一部分课后笔记

一、JVM优化

1. JVM概述

d9ebb5633cae

image.png

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

JVM 是 Java 程序能够运行的核心。但是需要注意,JVM 自己什么也干不了,你需要给它提供生产原料(.class 文件) 。仅仅是 JVM,是无法完成一次编译,处处运行的。它需要一个基本的类库,比如怎么操作文件、怎么连接网络等。

JVM 标准加上实现的一大堆基础类库,就组成了 Java 的运行时环境,也就是我们常说的 JRE(Java Runtime Environment)。

对于 JDK (Java Development Kit)来说,就更庞大了一些。除了 JRE,JDK 还提供了一些非常好用的小工具,比如 javac、java、jar 等。

JVM、JRE、JDK 它们三者之间的关系,可以用一个包含关系表示:

d9ebb5633cae

image.png

2. JVM整体架构

根据 JVM 规范,JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。

d9ebb5633cae

image.png

d9ebb5633cae

image.png

JVM分为五大模块: 类装载器子系统 、 运行时数据区 、 执行引擎 、 本地方法接口 和 垃圾收集模块 。

d9ebb5633cae

image.png

JDK7和JDK8变化

d9ebb5633cae

image.png

线程私有的: ①程序计数器 ②虚拟机栈 ③本地方法栈

线程共享的: ①堆 ②方法区 直接内存(非运行时数据区的一部分)

方法区Java8之后的变化

移除了永久代(PermGen),替换为元空间(Metaspace)

永久代中的class metadata(类元信息)转移到了native memory(本地内存,而不是虚拟机)

永久代中的interned Strings(字符串常量池) 和 class static variables(类静态变量)转移到了Java heap

永久代参数(PermSize MaxPermSize)-> 元空间参数(MetaspaceSize MaxMetaspaceSize)

Java8为什么要将永久代替换成Metaspace?

字符串存在永久代中,容易出现性能问题和内存溢出。

类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。

永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

Oracle 可能会将HotSpot 与 JRockit 合二为一,JRockit没有所谓的永久代。

二、OutOfMemoryError异常

1. Java堆溢出

堆内存中主要存放对象、数组等,只要不断地创建这些对象,并且保证 GC Roots 到对象之间有可达路径来避免垃圾收集回收机制清除这些对象,当这些对象所占空间超过最大堆容量时,就会产生 OutOfMemoryError 的异常。

/*** 设置最大堆最小堆:-Xms20m -Xmx20m */

public class HeapOOM {

static class OOMObject {

}

public static void main(String[] args {

List oomObjectList = new ArrayList<>();

while (true) { oomObjectList.add(new OOMObject());

}

}

}

运行后会报异常,在堆栈信息中可以看到:

java.lang.OutOfMemoryError: Java heap space 的信息,

说明在堆内存空间产生内存溢出的异常。

新产生的对象最初分配在新生代,新生代满后会进行一次 Minor GC ,如果 Minor GC 后空间不足会把该对象和新生代满足条件的对象放入老年代,老年代空间不足时会进行 Full GC ,之后如果空间还不足以存放新对象则抛出 OutOfMemoryError 异常。

常见原因:

内存中加载的数据过多,如一次从数据库中取出过多数据;

集合对对象引用过多且使用完后没有清空;

代码中存在死循环或循环产生过多重复对象;

堆内存分配不合理

2. 虚拟机栈和本地方法栈溢出

由于HotSpot虚拟机中并不区分虚拟机栈和本地方法栈, 因此对于HotSpot来说, -Xoss参数(设置本地方法栈大小) 虽然存在, 但实际上是没有任何效果的, 栈容量只能由-Xss参数来设定。

常见原因:

1) 如果线程请求的栈深度大于虚拟机所允许的最大深度, 将抛出StackOverflowError异常。

2) 如果虚拟机的栈内存允许动态扩展, 当扩展栈容量无法申请到足够的内存时, 将抛出OutOfMemoryError异常。

对于不同版本的Java虚拟机和不同的操作系统, 栈容量最小值可能会有所限制, 这主要取决于操 作系统内存分页大小。

譬如参数-Xss128k可以正常用于32位Windows系统下的JDK 6, 但是如果用于64位Windows系统下的JDK 11, 则会提示栈容量最小不能低于180K, 而在Linux下这个值则 可能是228K, 如果低于这个最小限制, HotSpot虚拟器启动时会给出如下提示:

The Java thread stack size specified is too small. Specify at least 228k

3. 运行时常量池和方法区溢出

在JDK 6或更早之前的HotSpot虚拟机中, 常量池都是分配在永久代中;

我们可以通过-XX: PermSize和-XX: MaxPermSize限制永久代的大小, 即可间接限制其中常量池的容量。

自JDK 7起, 原本存放在永久代的字符串常量池被移至Java堆之中,不容易溢出。

方法区的主要职责是用于存放类型的相关信息, 如类名、 访问修饰符、 常量池、 字段

描述、 方法描述等。

方法区溢出:方法区运行时产生大量的类。

一个类如果要被垃圾收集器回收, 要达成的条件是比较苛刻的。

在经常运行时生成大量动态类的应用场景里, 就应该特别关注这些类的回收状况。

这类场景除了使用了CGLib字节码增强和动态语言外, 常见的还有: 大量JSP或动态产生JSP 文件的应用(JSP第一次运行时需要编译为Java类) 、 基于OSGi的应用(即使是同一个类文件, 被不同的加载器加载也会视为不同的类)等。

4. 直接内存溢出

直接内存(Direct Memory) 的容量大小可通过-XX: MaxDirectMemorySize参数来指定, 如果不去指定, 则默认与Java堆最大值(由-Xmx指定) 一致;

由直接内存导致的内存溢出, 一个明显的特征是在Heap Dump文件中不会看见有什么明显的异常情况, 如果发现内存溢出之后产生的Dump文件很小, 而程序中又直接或间接使用了 DirectMemory(典型的间接使用就是NIO) ,那就可以考虑重点检查一下直接内存方面的原因了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值