java内存概述

news2024/11/15 19:02:05
  1. 运行时数据区域

Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启 动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。

在这里插入图片描述

  1. 程序计数器

​ 程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号****指示器。在 Java 虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码****指令,它是程序控制 流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

Java 虚拟机的多线程****是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的****程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器值则应为空 (Undefined)。此内存区域是唯一一个在《Java 虚拟机规范》中没有规定任何 OutOfMemoryError (内存耗尽)情况的区域。

Java方法

  • 定义:Java方法是用Java语言编写的方法。这些方法的字节码由Java虚拟机(JVM)执行。
  • 运行环境:在Java虚拟机内部执行,使用JVM字节码解释器或JIT编译器进行运行。
  • 内存****分配:使用JVM管理的内存(如堆(Heap)、方法区(Method Area)等)。
  • 错误处理:如果内存不足,可能会抛出OutOfMemoryError异常,例如,当JVM堆或方法区耗尽内存时。
  • 应用场景:适用于绝大多数需要在Java环境下运行的逻辑。

本地方法

  • 定义:本地方法是用非Java语言编写的方法(如C或C++),并通过Java本地接口(JNI)从Java代码中调用。
  • 运行环境:在Java虚拟机外部执行,直接运行在宿主机的硬件上。
  • 内存****分配:不仅使用由JVM管理的内存,还可能使用由本地程序运行环境管理的内存。
  • 错误处理:虽然在本地方法执行过程中不会直接由JVM抛出OutOfMemoryError,但如果本地代码分配内存时资源不足,仍可能导致错误或程序崩溃。
  • 应用场景:通常用于需要直接访问系统资源或执行系统级操作、优化性能的场景,或是为了使用已有的非Java库。
  1. Java 虚拟机栈

​ Java 虚拟机栈(Java Virtual Machine Stack)也是线程私有的, 它的生命周期与线程相同。虚拟机栈描述的是 Java 方法执行的线程内存模型:每个方法 被执行的时候,Java 虚拟机都会同步创建一个****栈帧用于存储局部变量 表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程, 就对应着一个栈帧在虚拟机栈中从入栈出栈的过程。

​ 经常有人把 Java 内存区域笼统地划分为堆内存(Heap)和栈内存(Stack),这样是很粗糙的,实际的内存划分比这个复杂,这里的栈通常就是指这里讲的虚拟机栈,或者更多的情况下只是指虚拟机栈中局部变量表部分。

局部变量****表:

局部变量表存放了编译期可知的各种 Java 虚拟机基本数据类型,对象引用(reference 类型,它并不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和 returnAddress 类型(指向了一条字节码指令的地址)。

这些数据类型在**局部变量****表中的存储空间以局部变量槽(Slot)**来表示,其中 64 位 长度的 long 和 double 类型的数据会占用两个变量槽,其余的数据类型只占用一个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大 小。请读者注意,这里说的“大小”是指变量槽的数量,虚拟机真正使用多大的内存空间 (譬如按照 1 个变量槽占用 32 个比特、64 个比特,或者更多)来实现一个变量槽,这 是完全由具体的虚拟机实现自行决定的事情

  1. 本地方法栈

    本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。 《Java 虚拟机规范》对本地方法栈中方法使用的语言、使用方式与数据结构并没有任何强制规定,因此具体的虚拟机可以根据需要自由实现它,甚至有的 Java 虚拟机(譬 如 Hot-Spot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地 方法栈也会在栈深度溢出或者栈扩展失败时分别抛出 StackOverflowError 和 OutOfMemoryError 异常。

  2. Java 堆

​ 对于 Java 应用程序来说,Java 堆是虚拟机所管理的内存中最大的一 块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java 世界里“几乎”所有的对象实例都在这里分配内存。

Java 堆是垃圾收集器管理的内存区域

如果从分配内存的角度看,所有线程共享的 Java 堆中可以划分出多个线程私有的分配缓冲区,以提升对象分配时的效率。无论如何划分,都不会改变 Java 堆中存储内容的共性,无论是哪个区 域,存储的都只能是对象的实例,将 Java 堆细分的目的只是为了更好地回收内存,或 者更快地分配内存

Java 堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的

Java 堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的 Java 虚拟 机都是按照可扩展来实现的

  1. 方法区

方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。 虽然《Java 虚拟机规范》中把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫 作“非堆”(Non-Heap),目的是与 Java 堆区分开来。

《Java 虚拟机规范》对方法区的约束是非常宽松的,除了和 Java 堆一样不需要连续的内存和可以选择固定大小或者可扩展外,甚至还可以选择不实现****垃圾收集。相对而言,垃圾收集行为在这个区域的确是比较少出现的,但并非数据进入了方法区就如永久代的名字一样“永久”存在了。这区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收有时又确实是必要的。以前 Sun 公司的 Bug 列表中,曾出现过的若干个严重的 Bug 就是由于低版本的 HotSpot 虚拟机对此区域未完全回收而导致内存泄漏。

运行时常量池

运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中

运行时常量池
  • 概念:运行时常量池是方法区的一部分,用于**存放编译期生成的各种字面量(如文字字符串、final常量等)**和符号引用(如类和接口的全限定名、字段的名称和描述符、方法的名称和描述符等)。
  • 动态性它具有动态性,意味着不仅包含Class文件中编译期间生成的常量,也可以在运行期间接受新的常量(比如String的intern()方法可以将字符串添加到运行时常量池中)。
  • 内存****限制:因为运行时常量池是方法区的一部分,它的大小和方法区的内存大小直接相关。如果方法区内存不足,运行时常量池也无法扩展,会抛出OutOfMemoryError异常。
Class文件常量池
  • 概念:Class文件常量池是Class文件结构的一部分,它包含了类中所有的编译期常量信息,比如上文提及的字面量和符号引用。
  • 作用:Class文件常量池为类定义中的所有名字和其他常量提供了索引和引用支持。
关系

Class文件常量池是静态的,它存储在磁盘上的Class文件里;而运行时常量池是动态的,它存储在JVM内存的方法区里。二者的主要区别在于存储位置和动态性,但它们都是用来存储类中的常量信息的。运行时常量池是Class文件常量池在JVM内存中的一种运行时表现形式。

直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是《Java 虚 拟机规范》中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致 OutOfMemoryError 异常出现,所以我们放到这里一起讲解。

​ 本机直接内存的分配不会受到 Java 堆大小的限制,但是,既然是内存,则肯 定还是会受到本机总内存(包括物理内存、SWAP 分区或者分页文件)大小以及处理器 寻址空间的限制,一般服务器管理员配置虚拟机参数时,会根据实际内存去设置-Xmx 等参数信息,但经常忽略掉直接内存,使得各个内存区域总和大于物理内存限制(包括 物理的和操作系统级的限制),从而导致动态扩展时出现 OutOfMemoryError 异常。

总结

  • 程序计数器:字节码的行号指示器,明确下一步命令执行什么,每个线程独立
  • java虚拟机栈:存放java方法,当一个方法开始执行创建栈帧,执行结束栈帧出栈局部变量表中以局部变量槽为对象存储数据,线程独立
  • 本地方法区:和上面类似,不过是处理本地方法
  • java堆:存放对象实例,所有线程共享
  • 方法区:存储已经加载的类型信息,常量,静态变量,代码缓存等信息,所有线程共享
  1. HotSpot 虚拟机对象探秘

这里具体的讨论一下HotSpot 虚拟机在java堆中的对象分配,布局,访问的全过程

  1. 对象的创建

在我们使用的时候创建一个对象一般(比如复制就不是)是new一个对象,那么在虚拟机中是如何实现的

在虚拟机中,遇到一个new命令,首先会检查这个指令参数是否可以在常量池中定位到一个类的引用,同时会检查她是否已经被加载,解析,初始化过,如果没有那么先执行类加载过程

在类加载检查通过后,接下来虚拟机会新的对象分配内存,这里内存的大小在类加载完即可确定,这里分配对象是等同于将一块确定大小的内存从java堆中划分出来,这里如何分配内存有俩种方法,一个是指针碰撞,一个是空闲列表

  • 指针碰撞:如果在java堆中内存是规整的,使用过和没使用过的内存中间会有一个指针作为分界器的指示器,这里的分配内存就是将这个指针向空闲的方向移动确定距离
  • 空闲联表:如果内存不是规整的,那么虚拟机需要维护一个列表记录那些内存块是可用的,分配的时候将足够大的空间分配给对象实例,在列表上更新内容

这里的**分配方式是由java对内存是否规整确定的,而java堆是否规整又是由垃圾回收器是否带有空间压缩整理功能决定的,**所以不同的垃圾回收器决定了它使用那种分配方式

确定了划分空间后,需要知道,对象创建在java虚拟机中是非常频繁的,如果只是改变指针指向位置分配内存,会有并发问题,即在给对象A分配内存,指针还未修改,对象B已经开始使用原来的指针分配内存,这里有俩种解决方法

一个是对分配空间的动作进行同步处理(实际上虚拟机是采用 CAS (一种配合硬件支持的保证原子性操作)配上失败重试的方式保证更新操作 的原子性)

一个是把内存的分配动作根据线程划分在不同的空间中进行,在java堆每一个线程预先分配一块内存,称为本地线程分配缓冲(TLAC),那个线程本地缓冲区分配完毕,分配新的缓冲区的时候才会同步锁定,虚拟机是否使用TLAC可以通过参数设置

内存分配完毕后,虚拟机****必须将分配到的内存初始化为零值,如果使用了本地线程分配缓冲这个工作也可以在分配时顺便执行,

到了这里,虚拟机还需要对对象进行必要的设置,将一些信息放置到对象的对象头,根据虚拟机的不同会有不同的对象头设置方式

到这里对象的创建基本结束,但是在java程序中,还有其他操作,比如构建函数的处理,即Class文件中的init()方 法还没有执行,所有的字段都为默认的零值,对象需要的其他资源和状态信息也还没有按照预定的意图构造好。一般来说(由字节码流中 new 指令****后面是否跟随 invokespecial 指令所决定,Java 编译器会在遇到 new 关键字的地方同时生成这两条字节码指令,但如果直接通过其他方式产生的则不一定如此),new 指令之后会接着执行 ()方法,按照程序员的意愿对对象进行初始化,这样一个真正可用的对象才算完全被构造出来

总结:首先分配地址,有俩种分配方法,然后是并发问题,再者是为空间赋零值,最后是将一些信息放入到对线头中进行存储,到这里对象基本创建完毕,但是还有他的构建方法,执行init方法

  1. 对象的内存布局

在Java中,一个对象在内存中由三部分组成:对象头,实例数据,以及对齐填充

  • 对象头

对象头包含了对象的一些元数据,主要用于运行时的管理。它通常包括以下几个部分:

  • Mark Word(标记字段):用于存储对象的运行时状态信息,如哈希码(HashCode)、GC分代年龄、锁状态标志等,这部分数据的长度在 32 位和 64 位的虚拟机中分别为 32 个比特和 64 个比特。

  • Class Pointer(类型指针):指向对象的类定义,即指向元数据部分的指针,这样 JVM 可以通过这个指针获取对象的类信息。

  • Array Length(数组长度):如果对象是一个数组,对象头中还包含一个数组长度的字段,用于快速访问数组中的元素数量。

  • 实例数据(Instance Data)

​ 实例数据部分存储对象的实际数据,即类的属性内容(包括从父类继承的内容)。存储顺序受虚拟机分配参数和字段在java代码中的定义顺序

  • 对齐填充(Padding)

对齐填充不是必然存在的,它用于确保对象的大小是机器地址位数的整数倍,这样有助于提高内存访问的效率。

实例数据的默认分配机制

HotSpot 虚拟机默认的分配顺序为 longs/doubles、ints、shorts/chars、 bytes/booleans、oops(Ordinary Object Pointers,OOPs),从以上默认的分配策略中可以 看到,相同宽度的字段总是被分配到一起存放,在满足这个前提条件的情况下,在父类 中定义的变量会出现在子类之前。如果 HotSpot 虚拟机的+XX:CompactFields 参数值为 true(默认就为 true),那子类之中较窄的变量也允许插入父类变量的空隙之中,以节省 出一点点空间。

  1. 对象的访问定位

前面创建了对象,我们在后续使用对象的时候虚拟机需要找到这个对象,这里Java 程序会通过栈上的 reference 数据来操作堆上的具体对象,reference 类型在《Java 虚拟机规范》里面只规定了它是 一个指向对象的引用,并没有定义这个引用应该通过什么方式去定位、访问到堆中对象的具体位置,所以对象访问方式也是由虚拟机实现而定的,主流的访问方式主要有使用句柄和直接指针两种:

使用句柄

Java 堆中将可能会划分出一块内存来作为句柄池, reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息

如果使用直接指针访问的话,Java 堆中对象的内存布局就必须考虑如何放置访问 类型数据的相关信息,reference 中存储的直接就是对象地址,如果只是访问对象本身的 话,就不需要多一次间接访问的开销

这两种对象访问方式各有优势,使用句柄来访问的最大好处就是 reference 中存储的是稳定句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而 reference 本身不需要被修改。
在这里插入图片描述

直接指针:直接指针来访问最大的好处就是速度更快,它节省了一次指针定位的时间开销,这里的HotSpot虚拟机使用了它,但是在其他地方第一中也有不少使用

  1. OOM异常(内存耗尽)

  2. java堆异常

java堆存储对象实例,只需要不停创建对象,然后保证GC Roots(垃圾收集器)到对象之间有可达路径避免垃圾回收机制清除对象,那么对象不停增加,总容量到达最大堆的容量限制之后和产生内存溢出

while (true) {  
    list.add(new OOMObject()); 
}

到这里,它最后会报错,OutOfMemoryError,要解决这个问题,处理方法是:通过内存****映像分析工具对 Dump 出来的堆转储快照进行分析。第一步首先应确认内存中导致 OOM 的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏 还是内存溢出

如果不是内存泄漏,就是这些对象都是需要的,那么应该检查java虚拟机的堆参数设置,与机器的内存比较,看是否有可以继续上调空间,再在代码上检查是否有些对象设计不合理等,尽量减少程序内存消耗

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

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

两种常见异常:

  1. 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError 异常。
  2. 如果虚拟机的栈内存允许动态扩展,当扩展栈容量无法申请到足够的内存时, 将抛出 OutOfMemoryError 异常。

《Java 虚拟机规范》明确允许 Java 虚拟机实现自行选择是否支持栈的动态扩展,而 HotSpot 虚拟机的选择是不支持扩展,所以除非在创建线程申请内存时就因无法获得足够内存而出现 OutOfMemoryError 异常,否则在线程运行时是不会因为扩展而导致内存溢出的,只会因为栈容量无法容纳新的栈帧而导致 StackOverflowError 异常。

这里有两种方法使其爆出异常,一个是:使用-Xss 参数减少 栈内存容量。结果:抛出 StackOverflowError 异常,异常出现时输出的堆栈深度相应缩 小。

public class JavaVMStackSOF {
    private int stackLength = 1;
    public void stackLeak() {
        stackLength++;
        stackLeak();
    }
    public static void main(String[] args) throws Throwable {
        JavaVMStackSOF oom = new JavaVMStackSOF();
        try {
            oom.stackLeak();
        } catch (Throwable e) {
            System.out.println("stack length:" + oom.stackLength);
            throw e;
        }
    }
}

运行结果

在这里插入图片描述

另一个是:定义了大量的本地变量,增大此方法帧中本地变量表的长度。

这个代码是在方法定义很多变量,消耗他的本地变量表

无论是由于栈帧太大还是虚拟机栈容量太小,当新的栈帧内存无法 分配的时候, HotSpot 虚拟机抛出的都是 StackOverflowError 异常,这里的HotSpot是不支持动态扩展内存的,在一些支持动态扩展内存的虚拟机中保存的错误将会是内存耗尽,

如果通过不断建立线程的方式,在HotSpot中也会出现内存溢出,但是这样产生的内存溢出异常和栈空间是否足够并不存在任何直接的关系,主要取决于操作系统本身的内存使用状态。甚至可以说,在这种情况下,给每个线程的栈分配的内存越大,反而越容易产生内存溢出异常。操作系统分配给每个进程的内存是有限制的

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

由于运行时常量池是方法区的一部分,所以这两个区域的溢出测试可以放到一起进行

  • 永久代:在java虚拟机中在非堆内存中,和堆内存一样受到物理内存大小的限制
  • 元空间:元空间使用的是本地内存(Native Memory),而不是虚拟机内存

二者区别

  1. 内存管理:元空间由于使用本地内存,理论上可以拥有更大的空间,减少了内存溢出的风险。
  2. 性能:元空间的引入改善了类的加载和卸载机制,可能会提高程序的性能。
  3. 垃圾回收:元空间的垃圾回收更加高效,有助于优化内存使用。

String::intern()是一个本地方法,它的作用是如果**字符串常量池(JDK7之前他是方法区永久代的一部分,JDK8永久代被移除它被移动到了java堆中)**中已经包含一个等于此 String 对象的字符串,则返回代表池中这个字符串的 String 对象的引用;否则,会将 此 String 对象包含的字符串添加到常量池中,并且返回此 String 对象的引用

public class RuntimeConstantPoolOOM {
    public static void main(String[] args) {
        // 使用 Set 保持着常量池引用,避免 Full GC 回收常量池行为
        Set<String> set = new HashSet<String>(); // 在 short 范围内足以让 6MB 的
        short i = 0;
        while (true) {
            set.add(String.valueOf(i++).intern());
        }
    }
}

这里的字符串通过intern方法存储到了字符串常量池中,然后导致OOM异常

public class RuntimeConstantPoolOOM {
    public static void main(String[] args) {
        String str1 = new StringBuilder("计算机").append("软件").toString();
        System.out.println(str1.intern() == str1);
        String str2 = new StringBuilder("ja").append("va").toString();
        System.out.println(str2.intern() == str2);
    }
}

这里的代码在不同的Jdk的返回值不同,如果是jdk6和以前他会返回俩个false,如果是jdk7中,他的返回值是一个true一个false,这里解释为什么,

首先在jdk6之前,intern()方法会首先检查在字符串常量池中是否由这个实例,然后创建一个在字符串常量池中,这里的字符串常量池不在java堆中,在方法区的永久代中,所以它会返回一个创建在永久代中的实例引用,而StringBuilder 创建的字符串对象实例在 Java 堆上,所以二者不同会打印false,str1和str2都是这个原因

其次在jdk7之后,字符串常量池在java堆中,第一次检查的时候就会在java堆中找到,不会创建新的实例所以返回的结果是true,而java在之前已经创建过了,而StringBuilder会创建一个新的实例,intern()方法会返回第一个实例所以俩个不同返回false

我们再来看看方法区的其他部分的内容,方法区的主要职责是用于存放类型的相关 信息,如类名、访问修饰符、常量池、字段描述、方法描述等。对于这部分区域的测试,基本的思路是运行时产生大量的类去填满方法区,直到溢出为止。借助了 CGLib[3]直接操 作字节码运行时生成了大量的动态类。这种情况不光是实验在很多框架中也会出现这种情况如: Spring、Hibernate 对类 进行增强时,都会使用到 CGLib 这类字节码技术,当增强的类越多,就需要越大的方法 区以保证动态生成的新类型可以载入内存

方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收,要达 成的条件是比较苛刻的。在经常运行时生成大量动态类的应用场景里,就应该特别关注 这些类的回收状况。

在 JDK 8 以后,永久代便完全退出了历史舞台,元空间作为其替代者登场。在默认设置下,前面列举的那些正常的动态创建新类型的测试用例已经很难再迫使虚拟机产生 方法区的溢出异常了。不过为了让使用者有预防实际应用里出现破坏性的操作,HotSpot 还是提供了一些参数作为元空间的防御措施,主要包括:

  • MaxMetaspaceSize:设置元空间最大值,默认是-1,即不限制,或者说只受限于本地内存大小。
  • MetaspaceSize:指定元空间的初始空间大小,以字节为单位,达到该值就会触发垃圾收集进行类型卸载,同时收集器会对该值进行调整:如果释放了大量的空间, 就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize(如 果设置了的话)的情况下,适当提高该值。
  • MinMetaspaceFreeRatio:作用是在垃圾收集之后控制最小的元空间剩余容量的百分比,可减少因为元空间不足导致的垃圾收集的频率。类似的还有-XX:MaxMetaspaceFreeRatio,用于控制最大的元空间剩余容量的百分比。
  1. 本机直接内存溢出

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

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

总结:

java虚拟机一共五块内存区域,程序计数器,java虚拟机栈,本地方法栈,java堆,方法区,这里的程序计数器不需要关注OOM问题,其他四块区域:

  • java堆:存储对象,如果对象过多他会通过内存映像分析工具来处理,首先他会分析内存对象是否是必须的,分析他是内存泄漏还是内存溢出,如果是内存溢出比较java虚拟机内存参数与本地内存,看是否可以上调内存大小
  • java虚拟机栈和本地方法栈:他有俩个异常,一个是栈数过深,一个是内存溢出,这里虚拟机可以在设计的时候自己选择是否支持动态扩展,如果不支持那么只能在一开始创建的时候爆出内存溢出 。栈帧太大还是虚拟机栈容量太小,当新的栈帧内存无法 分配的时候, HotSpot 虚拟机抛出的都是 StackOverflowError 异常,这里的HotSpot是不支持动态扩展内存的,在一些支持动态扩展内存的虚拟机中保存的错误将会是内存耗尽,不断建立线程也会出现内存溢出的情况
  • 方法区和运行时常量池溢出:主要是元空间和永久代的问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2138835.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

charls基于夜神模拟器抓取安卓7.0应用程序https请求

charls基于夜神模拟器抓取安卓7.0应用程序https请求 1、安装charls&#xff08;安装步骤这里就不详细说了&#xff09;2、下载证书&#xff08;证书后缀名 xx.pem&#xff09;3、使用git bash生成证书hash4、上传证书到安卓的系统证书目录下&#xff08;夜神模拟器方案&#xf…

C++速通LeetCode简单第9题-二叉树的最大深度

深度优先算法递归&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right…

屏幕缺陷检测-目标检测数据集(包括VOC格式、YOLO格式)

屏幕缺陷检测-目标检测数据集&#xff08;包括VOC格式、YOLO格式&#xff09; 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1mb83CzAAOkvMZ_LS9Alt8w?pwdagi6 提取码&#xff1a;agi6 数据集信息介绍&#xff1a; 共有 3789 张图像和一一对应的标注文件 标…

RPC远程调用的序列化框架

序列化框架对比&#xff1a; 一、Java Serialiazer 字段serialVersionUID的作用是为了在序列化时保持版本的兼容性&#xff0c;即版本升级时反序列化仍保持对象的唯一性。 //序列化 ByteArrayOutputStream bout new ByteArrayOutputStream(); ObjectOutoutStream out new O…

【Kubernetes笔记】为什么DNS解析会超时?

【Kubernetes笔记】为什么DNS解析会超时&#xff1f; 目录 1 问题背景2 产生后续的问题3 DNS 负缓存工作原理&#xff1a;4 如何解决和缓解 DNS 负缓存 4.1 减小负缓存 TTL4.2 重试机制4.3 减少 Pod 的频繁重启或调度4.4 使用 Headless Service4.5 手动刷新 DNS 缓存 5 总结 …

苹果cms多语言插件,插件配置前端默认语言采集语言等

苹果CMS&#xff08;maccmscn&#xff09;是一款功能强大的内容管理系统&#xff0c;广泛应用于视频网站和其他内容发布平台。为了满足全球用户的需求&#xff0c;苹果CMS支持多语言插件&#xff0c;使得网站能够方便地提供多语言版本。以下是关于苹果CMS多语言插件的详细介绍&…

网络原理2-网络层与数据链路层

目录 网络层数据链路层 网络层 网络层做的工作&#xff1a; 1、地址管理–>IP地址 2、路由选择–>数据包传输的路径规划 网络层主要的协议就是IP协议 IP协议的报头结构&#xff1a; 4位版本&#xff1a; 有两个取值&#xff0c;4表示IPv4&#xff0c;6表示IPv6&am…

关于网站ERR_TOO_MANY_REDIRECTS错误的修改办法

今天网站因为证书到期&#xff0c;七牛云的报错一直ERR_TOO_MANY_REDIRECTS&#xff0c;302重定向次数过多了&#xff0c;连后台都进不去 多次清除cookie未果&#xff0c;终于找到了原因&#xff1a;设置了太多重定向&#xff0c; 1.http强制跳转 2.宝塔后台设置了跳转 3.域…

备战软考Day02-数据结构与算法

1.基本概念与三要素 1.什么是数据 数据是信息的载体&#xff0c;是描述客观事物属性的数、字符及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。数据是计算机程序加工的原料。 2.数据元素、数据项 数据元素是数据的基本单位&#xff0c;通常作为一个整体进行…

树莓派Pico2(RP2350)开发环境搭建

树莓派Pico2(RP2350)开发环境搭建 文章目录 树莓派Pico2(RP2350)开发环境搭建1、RP2350介绍2、开发环境搭建3、工程编译4、固件下载Raspberry Pi再次通过推出RP2350 MCU突破了微控制器设计的界限。这款微控制器是之前RP2040的重大升级,带来了更强大的性能、高级安全功能,…

基于SSM的二手车管理系统的设计与实现 (含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的二手车管理系统4拥有三种角色 管理员&#xff1a;订单管理、在售车辆管理、下架车辆管理、品牌管理、分类管理、推荐管理、统计等 商家&#xff1a;登录注册、添加/下架/删除车辆…

vscode搭建ros开发环境问题记录(更新...)

文章目录 vscode 不能自动补全 开发环境&#xff1a; vmware 15.7 ubuntu 20.04 ros noetic vscode 不能自动补全 这里将头文件已经正确包含到c_cpp_properties.json中代码中仍然不能自动补全&#xff0c; 将C_CPP插件设置中的Intelli Sense Engine 设置为TagParser,然后重新加…

828华为云征文 | 云服务器Flexus X实例:部署 Gitea,拥有自己的Git仓库,管理本地代码

目录 一、什么是 Gitea 二、安装 Docker 环境 2.1 更新 apt 软件源 2.2 安装依赖 2.3 安装 Docker 三、安装 Gitea 3.1 创建 docker-compose.yml 3.2 启动 Gitea 服务 3.3 初始化配置 四、运行 Gitea 4.1 登录/注册 4.2 创建仓库 五、总结 本篇文章通过部署 Gite…

【BFS专题】— 多源最短路问题

1、矩阵 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 首先创建一个dist数组&#xff0c;将dist数组初始化为-1&#xff0c;表示该位置没有被搜索和记录该位置的值然后再遍历mat数组&#xff0c;将数组中的 0 添加到队列中&#xff0c;并且修改dist对应位置的值为…

全网最全最详细的跨域解决方案

你们好&#xff0c;我是金金金。 前置知识 本篇文章以通俗易懂的方式进行描述&#xff0c;自己组织语言进行输出&#xff0c;尽量让每一个人都能看得懂。哪里有说的不正确的地方 大佬请在评论区指正&#xff01; 首先需要了解浏览器的同源策略 浏览器的同源策略 MDN解释地址&…

神经网络通俗理解学习笔记(3)注意力神经网络

Tansformer 什么是注意力机制注意力的计算键值对注意力和多头注意力自注意力机制注意力池化及代码实现Transformer模型Transformer代码实现 什么是注意力机制 注意力机制的发展史 Attention Mechanism Mnih V, Heess N, Graves A. Recurrent models of visual attention, 2014…

JVM 调优篇7 调优案例1-堆空间的优化解决

一 jvm优化 1.1 优化实施步骤* 1)减少使用全局变量和大对象&#xff1b; 2)调整新生代的大小到最合适&#xff1b; 3)设置老年代的大小为最合适&#xff1b; 4)选择合适的GC收集器&#xff1b; 1.2 关于GC优化原则 多数的Java应用不需要在服务器上进行GC优化&#xff1…

NeMo Curator 整理用于 LLM 参数高效微调的自定义数据集

目录 概述 预备知识 定义自定义文档构建器 下载数据集 解析和迭代数据集 将数据集写入 JSONL 格式 使用文档构建器加载数据集 使用现有工具统一 Unicode 格式 设计自定义数据集过滤器 编辑所有个人识别信息 添加指令提示 整合管线 概述 出于演示目的&#xff0c;本…

【PyQt6 应用程序】应用程序携带数据源文件一并打包

在开发好应用程序打包之后给到其他用户会发现数据文件比如封面图片不见了。 例如这样,很影响用户使用。 这里介绍一个非常简单的打包方法,不光要在打包命令的时候添加对应数据文件,在源码中也要进行一些简单的修改。 修改需要添加打包文件的地方。首先需要添加一个绝对路径…

143234234123432

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…