JVM学习笔记七:堆

JVM堆详解 执行finalize()后,对象可能重新变为可达状态(例如,在finalize()方法中重新将自身赋值给某个静态变量),这种情况下,GC会重新对其进行可达性分析。原理:通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain)。Young GC主要在年轻代(Young Generation)触发,年轻代是存放新生成对象的区域,通常较小,并且分为Eden和两个Survivor区域(S0和S1)。 阅读详情

0. 前言

声明:
感谢尚硅谷宋红康老师的讲授。
感谢广大网友共享的笔记内容。
B站:https://www.bilibili.com/video/BV1PJ411n7xZ
本文的内容基本来源于宋老师的课件,其中有一些其他同学共享的内容,也有一些自己的理解内容。

1. 堆(Heap)的核心概述

堆对于一个 JVM 进程来说是唯一的,也就是一个进程只有一个 JVM,但是进程包含多个线程,他们是共享同一堆空间的。
JVM 内存模型——堆
一个 JVM 实例只存在一个堆内存,堆也是 Java 内存管理的核心区域。Java 堆区在 JVM 启动的时候即被创建,其空间大小也就确定了。是 JVM 管理的最大一块内存空间。

堆内存的大小是可以调节的。《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)。

《Java虚拟机规范》中对Java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上。(The heap is the run-time data area from which memory for all class instances and arrays is allocated)

数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置。

在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。

堆,是GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域。
栈堆方法区的关系

1.1 堆内存细分

在 Java7及其之前,堆内存在逻辑上分为:新生区、养老区、永久区

  • Young Generation Space 新生区 Young/New 又被划分为 Eden 区和 Survivor 区
  • Tenure generation space 养老区 Old / Tenure
  • Permanent Space 永久区 Perm

在 Java8 及其之后,堆内存在逻辑上分为:新生区、养老区、元空间

  • Young Generation Space 新生区 Young/New 又被划分为 Eden 区和 Survivor 区
  • Tenure generation space 养老区 Old / Tenure
  • Meta Space 元空间 Meta

名称的约定:新生区(代)<=> 年轻代、养老区 <=> 老年代、永久区 <=> 永久代

1.2 堆空间的内部结构(JDK7)

堆空间的内部结构

1.3 堆空间的内部结构JDK8

JDK8与之前的主要区别就是永久代改为了元空间。
堆空间的内部结构JDK8

2. 设置堆内存大小与 OOM

2.1 堆空间大小的设置

Java 堆区用于存储 Java 对象的实例,那么堆的大小在 JVM 启动时就已经设定好了,可以通过 -Xmx 和 -Xms` 来设置。

  • “-Xms" 用于表示堆区的起始内存,等价于-XX:InitialHeapSize。
  • “-Xmx" 则用于表示堆区的最大内存,等价于-XX:MaxHeapSize。

一旦堆区中的内存大小超过“-Xmx"所指定的最大内存时,将会抛出OutOfMemoryError异常。

通常会将-Xms和-Xmx两个参数配置相同的值,其目的是为了能够在 Java 垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能。

默认情况下:

  • 初始内存大小:物理电脑内存大小 / 64
  • 最大内存大小:物理电脑内存大小 / 4

3. 年轻代与老年代

存储在JVM中的Java对象可以被划分为两类:

  • 一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速。
  • 另外一类对象的生命周期却非常长,在某些极端的情况下还能够与 JVM 的生命周期保持一致。

Java堆区进一步细分的话,可以划分为年轻代(YoungGen)和老年代(oldGen)

其中年轻代分为 Eden 空间,Survivor0 与 Survivor1 空间(有时候也叫 from 区和 to 区),这里的 from 与 to 的位置是相对的,不是固定的,只代表对象实例经过 Minor GC 后从一个 Survivor 区域(From 区域)到另一个 Survivor 区域(To 区域)。
年轻代与老年代
下面这参数开发中一般不会调:
新生代与老年代在堆结构的占比
配置新生代与老年代在堆结构的占比。

默认 -XX:NewRatio = 2,表示新生代占1,老年代占2,新生代占整个堆的 1/3。可以修改 -XX:NewRatio = 4,表示新生代占1,老年代占4,新生代占整个堆的 1/5。

XX:NewRatio 设置的是老年代的占比,新生代占比固定为 1

在 HotSpot中,Eden 空间和另外两个survivor空间缺省所占的比例是8:1:1。

当然开发人员可以通过选项 -xx:SurvivorRatio 调整这个空间比例。比如 -xx:SurvivorRatio=8。

几乎所有的 Java 对象都是在 Eden 区被 New 出来的(也有部分例外)。绝大部分的 Java 对象的销毁在新生代就已经进行了。IBM公司的专门研究表明,**新生代中80%**的对象都是“朝生夕死”的。

可以使用选项"-Xmn"设置新生代最大内存大小,这个参数一般使用默认值就可以了。
分代结构

4. 图解对象分配过程

为新对象分配内存是一件非常严谨和复杂的任务,JVM的设计者们不仅需要考虑内存如何分配、在哪里分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需要考虑GC执行完内存回收后是否会在内存空间中产生内存碎片。

  1. new 的对象先放伊甸园区。此区有大小限制。
  2. 当伊甸园的空间填满时,程序又需要创建对象,JVM 的垃圾回收器将对伊甸园区进行垃圾回收(MinorGC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊甸园区
  3. 然后将伊甸园中的剩余对象移动到幸存者0区。
  4. 如果再次触发垃圾回收,此时上次幸存下来的放到幸存者0区的,如果没有回收,就会放到幸存者1区。
  5. 如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区。
  6. 啥时候能去养老区呢?可以设置次数。默认是15次。
    ○ 可以设置参数:进行设置-Xx:MaxTenuringThreshold= N
  7. 在养老区,相对悠闲。当养老区内存不足时,再次触发GC:Major GC,进行养老区的内存清理
  8. 若养老区执行了Major GC之后,发现依然无法进行对象的保存,就会产生OOM异常。 java.lang.OutofMemoryError: Java heap space

以上的过程可以用下图表示:
分代结构
在这里插入图片描述
流程图
在这里插入图片描述
总结

  • 针对幸存者 s0,s1 区的总结:复制之后有交换,谁空谁是 to。
  • 关于垃圾回收:频繁在新生区收集,很少在老年代收集,几乎不再永久代和元空间进行收集。

常用调优工具(在JVM下篇:性能监控与调优篇会详细介绍)

  • JDK命令行
  • Eclipse:Memory Analyzer Tool
  • Jconsole
  • VisualVM
  • Jprofiler
  • Java Flight Recorder
  • GCViewer
  • GC Easy

5. Minor GC,MajorGC、Full GC

JVM 在进行 GC 时,并非每次都对上面三个内存区域一起回收的,大部分时候回收的都是指新生代。针对Hotspot VM的实现,它里面的GC按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆收集(FullGC)。

  • 部分收集:不是完整收集整个 Java 堆的垃圾收集。其中又分为:
    • 新生代收集(Minor GC / Young GC):只是新生代的垃圾收集
    • 老年代收集(Major GC / Old GC):只是老年代的圾收集。
      • 目前,只有CMSGC会有单独收集老年代的行为。
      • 注意,很多时候 Major GC 会和 Full GC 混淆使用,需要具体分辨是老年代回收还是整堆回收。
    • 混合收集(MixedGC):收集整个新生代以及部分老年代的垃圾收集。 目前,只有G1 GC会有这种行为。
  • 整堆收集(Full GC):收集整个java堆和方法区的垃圾收集。

5.1 最简单的分代式GC策略的触发条件

5.1.1 年轻代GC(Minor GC)触发机制

当年轻代空间不足时,就会触发 MinorGC,这里的年轻代满指的是 Eden代 满,Survivor满不会引发GC。(每次Minor GC会清理年轻代的内存)

因为Java对象大多都具备朝生夕灭的特性.,所以Minor GC非常频繁,一般回收速度也比较快。这一定义既清晰又易于理解。

Minor GC会引发STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行。

MinorGC

5.1.2 老年代GC(Major GC / Full GC)触发机制

指发生在老年代的GC,对象从老年代消失时,我们说 “Major GC” 或 “Full GC” 发生了。

出现了 Major GC,经常会伴随至少一次的 Minor GC(但非绝对的,在 Paralle1 Scavenge 收集器的收集策略里就有直接进行 MajorGC 的策略选择过程),也就是在老年代空间不足时,会先尝试触发 Minor GC。如果之后空间还不足,则触发 Major GC。

Major GC的速度一般会比Minor GC慢10倍以上,STW的时间更长。如果Major GC后,内存还不足,就报OOM了。

5.1.3 Full GC触发机制(后面细讲)

触发Full GC执行的情况有如下五种:

  1. 调用System.gc()时,系统建议执行 Full GC ,但是不必然执行
  2. 老年代空间不足
  3. 方法区空间不足
  4. 通过 Minor GC 后进入老年代的平均大小 < 老年代的可用内存
  5. 由 Eden区、survivor space0(From Space)区向survivor space1(To Space)区复制时,对象大小 < To Space 可用内存,则把该对象转存到老年代,且老年代的可用内存 < 该对象大小。
    以上几条都可以归结为一句话:面对整个JVM的堆内存,对象大或者多的实在放不下了,赶紧 Full GC 吧。

注意:Full GC 是开发或调优中尽量要避免的。这样暂时时间会短一些

6. 堆空间分代思想

为什么要把Java堆分代?不分代就不能正常工作了吗?其实不分代完全可以,分代的唯一理由就是优化GC性能。不同对象的生命周期不同。70%-99%的对象是临时对象,进行分代之后,可以针对一块区域进行高频回收,其他区域非高频回收,这样有助于垃圾回收的性能与效率。

  • 新生代:有Eden、两块大小相同的survivor(又称为from/to,s0/s1)构成,to总为空。
  • 老年代:存放新生代中经历多次 GC 仍然存活的对象。

7. 内存分配策略

如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到survivor空间中,并将对象年龄设为1。对象在survivor区中每熬过一次MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁,其实每个JVM、每个GC都有所不同)时,就会被晋升到老年代。

对象晋升老年代的年龄阀值,可以通过选项 -XX:MaxTenuringThreshold 来设置。针对不同年龄段的对象分配原则如下所示:

  • 优先分配到Eden
  • 大对象直接分配到老年代(尽量避免程序中出现过多的大对象)
  • 长期存活的对象分配到老年代
  • 动态对象年龄判断:如果 Survivor 区中相同年龄的所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等到 MaxTenuringThreshold 中要求的年龄。
  • 空间分配担保: -XX:HandlePromotionFailure。

8. 为对象分配内存:TLAB

8.1 为什么有TLAB(Thread Local Allocation Buffer)?

  • 堆区是线程共享区域,任何线程都可以访问到堆区中的共享数据。
  • 由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的。
  • 为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度。

8.2 什么是TLAB

简单的说就是一块位于堆内存中的线程私有的区域,该区域可以一定程度的避免部分非线程安全问题。具体介绍如下:

从内存模型而不是垃圾收集的角度,对Eden区域继续进行划分,JVM为每个线程分配了一个私有缓存区域,它包含在Eden空间内。

多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为快速分配策略。 所有OpenJDK衍生出来的JVM都提供了TLAB的设计。
TLAB的描述

  • 尽管不是所有的对象实例都能够在TLAB中成功分配内存,但JVM确实是将TLAB作为内存分配的首选。
  • 在程序中,开发人员可以通过选项 -XX:UseTLAB 设置是否开启TLAB空间。
  • 默认情况下,TLAB空间的内存非常小,仅占有整个Eden空间的1%,当然我们可以通过选项 -XX:TLABWasteTargetPercent 设置TLAB空间所占用 Eden 空间的百分比大小。
  • 一旦对象在TLAB空间分配内存失败时,JVM就会尝试着通过使用加锁机制确保数据操作的原子性,从而直接在 Eden 空间中分配内存。
    TLAB的使用流程

9. 小结:堆空间的参数设置

官网地址:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html

// 详细的参数内容会在JVM下篇:性能监控与调优篇中进行详细介绍,这里先熟悉下
-XX:+PrintFlagsInitial  //查看所有的参数的默认初始值
-XX:+PrintFlagsFinal  //查看所有的参数的最终值(可能会存在修改,不再是初始值)
-Xms  //初始堆空间内存(默认为物理内存的1/64)
-Xmx  //最大堆空间内存(默认为物理内存的1/4)
-Xmn  //设置新生代的大小。(初始值及最大值)
-XX:NewRatio  //配置新生代与老年代在堆结构的占比
-XX:SurvivorRatio  //设置新生代中Eden和S0/S1空间的比例
-XX:MaxTenuringThreshold  //设置新生代垃圾的最大年龄
-XX:+PrintGCDetails //输出详细的GC处理日志
//打印gc简要信息:①-Xx:+PrintGC ② - verbose:gc
-XX:HandlePromotionFalilure://是否设置空间分配担保

在发生 Minor GC 之前,虚拟机会检查老年代最大可用的连续空间是否 大于 新生代所有对象的总空间。

  • 如果大于,则此次Minor GC是安全的
  • 如果小于,则虚拟机会查看-XX:HandlePromotionFailure设置值是否允担保失败。
    • 如果HandlePromotionFailure=true,那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小。
      • 如果大于,则尝试进行一次Minor GC,但这次Minor GC依然是有风险的;
      • 如果小于,则改为进行一次Full GC。
    • 如果 HandlePromotionFailure=false,则改为进行一次Full Gc。

JDK6 Update 24之后 HandlePromotionFailure 这个参数已经不再使用了。其规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行 Minor GC,否则将进行 FullGC。

10. 堆是分配对象的唯一选择么?

在《深入理解Java虚拟机》中关于Java堆内存有这样一段描述:

随着JIT编译期的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。

在Java虚拟机中,对象是在Java堆中分配内存的,这是一个普遍的常识。但是,有一种特殊情况,那就是如果经过逃逸分析(Escape Analysis)后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配.。这样就无需在堆上分配内存,也无须进行垃圾回收了。这也是最常见的堆外存储技术。

此外,前面提到的基于OpenJDK深度定制的TaoBaoVM,其中创新的GCIH(GC invisible heap)技术实现off-heap,将生命周期较长的Java对象从heap中移至heap外,并且GC不能管理GCIH内部的Java对象,以此达到降低GC的回收频率和提升GC的回收效率的目的。

GC优化的目的就是尽可能的减少长 GC 的次数,提示GC回收的效率。

10.1 逃逸分析介绍

如何将堆上的对象分配到栈,需要使用逃逸分析手段。

这是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。

通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。

逃逸分析的基本行为就是分析对象动态作用域:

  • 当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸。
  • 当一个对象在方法中被定义后,它被外部方法所引用,则认为发生逃逸。例如作为调用参数传递到其他地方中。

举例:

// 没有发生逃逸
public void my_method() {
    V v = new V();
    // use v
    // ....
    v = null;
}

// 对象 sb 被返回到外部,发生了逃逸
public static StringBuffer createStringBuffer(String s1, String s2) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb;
}

没有发生逃逸的对象,则可以分配到栈上,随着方法执行的结束,栈空间就被移除,占用的空间也得到释放。

参数设置
在 JDK 6u23 版本之后,HotSpot 中默认就已经开启了逃逸分析。

如果使用的是较早的版本,开发人员则可以通过:

  • 选项 -XX:+DoEscapeAnalysis 显式开启逃逸分析
  • 通过选项 -XX:+PrintEscapeAnalysis 查看逃逸分析的筛选结果

结论
结论:开发中能使用局部变量的,就不要使用在方法外定义。

10.2 逃逸分析:编译器对代码的优化

使用逃逸分析,编译器可以对代码做如下优化:

一、栈上分配:将堆分配转化为栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会发生逃逸,对象可能是栈上分配的候选,而不是堆上分配。

栈上分配
JIT编译器在编译期间根据逃逸分析的结果,发现如果一个对象并没有逃逸出方法的话,就可能被优化成栈上分配。分配完成后,继续在调用栈内执行,最后线程结束,栈空间被回收,局部变量对象也被回收。这样就无须进行垃圾回收了。

二、同步省略:如果一个对象被发现只有一个线程被访问到,那么对于这个对象的操作可以不考虑同步。

同步省略
线程同步的代价是相当高的,同步的后果是降低并发性和性能。

在动态编译同步块的时候,JIT编译器可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。如果没有,那么JIT编译器在编译这个同步块的时候就会取消对这部分代码的同步。这样就能大大提高并发性和性能。这个取消同步的过程就叫同步省略,也叫锁消除。

public void f() {
	// hellis 在方法内创建,且只在方法内使用,对于任何线程来说都是一个新的锁。不存在安全问题。
    Object hellis = new Object();
    synchronized(hellis) {
        System.out.println(hellis);
    }
}

上述代码在 JIT 编译阶段会被优化掉,优化为:

public void f() {
    Object hellis = new Object();
	System.out.println(hellis);
}

三、分离对象或标量替换:有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在CPU寄存器中。

标量替换
标量(scalar)是指一个无法再分解成更小的数据的数据。Java中的原始数据类型就是标量。

相对的,那些还可以分解的数据叫做聚合量(Aggregate),Java中的对象就是聚合量,因为他可以分解成其他聚合量和标量。

在JIT阶段,如果经过逃逸分析,发现一个对象不会被外界访问的话,那么经过JIT优化,就会把这个对象拆解成若干个其中包含的若干个成员变量来代替。这个过程就是标量替换。

public static void main(String args[]) {
    alloc();
}
private static void alloc() {
    Point point = new Point(1,2);
    System.out.println("point.x" + point.x + ";point.y" + point.y);
}
class Point {
    private int x;
    private int y;
}

上述 alloc 方法中 point 对象没有发生逃逸,经过标量优化之后,会优化为:

private static void alloc() {
    int x = 1;
    int y = 2;
    System.out.println("point.x = " + x + "; point.y=" + y);
}

这样既可以减少堆内存的占用,也为栈上分配提供了很好的基础。

标量替换参数设置
参数 -XX:EliminateAllocations :开启了标量替换(默认打开),允许将对象打散分配到栈上。

上述代码在主函数中进行了1亿次alloc。调用进行对象创建,由于User对象实例需要占据约16字节的空间,因此累计分配空间达到将近1.5GB。如果堆空间小于这个值,就必然会发生GC。使用如下参数运行上述代码:

-server -Xmx100m -Xms100m 
-XX:+DoEscapeAnalysis 
-XX:+PrintGC 
-XX:+EliminateAllocations

介绍:

  • 参数 -server:启动Server模式,因为在server模式下,才可以启用逃逸分析。
  • 参数 -XX:+DoEscapeAnalysis:启用逃逸分析
  • 参数 -Xmx10m:指定了堆空间最大为10MB
  • 参数 -XX:+PrintGC:将打印Gc日志
  • 参数 -XX:+EliminateAllocations:开启了标量替换(默认打开),允许将对象打散分配在栈上,比如对象拥有id和name两个字段,那么这两个字段将会被视为两个独立的局部变量进行分配。

10.3 逃逸分析小结:逃逸分析并不成熟

关于逃逸分析的论文在1999年就已经发表了,但直到JDK1.6才有实现,而且这项技术到如今也并不是十分成熟。

逃逸分析本身也是一项耗时的过程,不能保证分析过程消耗一定优于其效率。比如:如果经过逃逸分析之后发现,对象都是逃逸了,那么这个过程的消耗就白白浪费

虽然这项技术并不十分成熟,但是它也是即时编译器优化技术中一个十分重要的手段。

注意到有一些观点,认为通过逃逸分析,JVM会在栈上分配那些不会逃逸的对象,这在理论上是可行的,但是取决于JVM设计者的选择。据我所知,Oracle Hotspot JVM中并未这么做,这一点在逃逸分析相关的文档里已经说明,所以可以明确所有的对象实例都是创建在堆上。

目前很多书籍还是基于JDK7以前的版本,JDK已经发生了很大变化,intern字符串的缓存和静态变量曾经都被分配在永久代上,而永久代已经被元数据区取代。但是,intern 字符串缓存和静态变量并不是被转移到元数据区,而是直接在堆上分配,所以这一点同样符合前面一点的结论:对象实例都是分配在堆上。

11. 总结

年轻代是对象的诞生、成长、消亡的区域,一个对象在这里产生、应用,最后被垃圾回收器收集、结束生命。

老年代放置长生命周期的对象,通常都是从survivor区域筛选拷贝过来的Java对象。当然,也有特殊情况,我们知道普通的对象会被分配在TLAB上;如果对象较大,JVM会试图直接分配在Eden其他位置上;如果对象太大,完全无法在新生代找到足够长的连续空闲空间,JVM就会直接分配到老年代。当GC只发生在年轻代中,回收年轻代对象的行为被称为MinorGc。

当GC发生在老年代时则被称为MajorGc或者FullGC。一般的,MinorGc的发生频率要比MajorGC高很多,即老年代中垃圾回收发生的频率将大大低于年轻代。

【JVM】jvm虚拟机中的堆 对于Java应用程序来说,Java堆(Java Heap)是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块区域,在虚拟机启动时创建。此内存区域的唯一的目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存。在《Java虚拟机规范》中对Java堆的描述是:“所有的对象实例以及数组都应当在堆上分配”。 阅读详情

相关推荐

Simulink单相桥式有源逆变电路仿真:从器件选型到波形分析(MATLAB 2018a实战)

本文基于MATLAB 2018a的Simulink平台,详细演示了单相桥式有源逆变电路的完整仿真流程。从Simscape/Power Systems库的器件选型、参数配置,到触发脉冲设置与电路连接,逐步指导搭建仿真模型。通过调整触发角α,直观展示了电路从整流到有源逆变的工作状态切换,并深入分析了负载电压、电流波形,以及逆变颠覆现象的产生条件与特征。

cake8的博客 1159

请你谈谈对于堆的理解

堆分为两种:最大堆和最小堆,两者的差别在于节点的排序方式。 在最大堆中,父节点的值比每一个子节点的值都要大。在最小堆中,父节点的值比每一个子节点的值都要小。这就是所谓的“堆属性”,并且这个属性对堆中的每一个节点都成立。 这是一个最大堆,因为每一个父节点的值都比其子节点要大。10 比 7 和 2 都大。7 比 5 和 1都大。 根据这一属性,那么最大堆总是将其中的最大值存放在树的根节点。而对于最小堆,根节点中的元素总是树中的最小值。堆属性非常有用,因为堆常常被当做优先队列使用,因为可以快速地访问到“最重要”

一个搬砖工人 369

别再手动对照了!用Python+ArcGIS Pro一键搞定三调数据转土地三大类

本文介绍如何利用Python和ArcGIS Pro实现三调数据向土地三大类的自动化转换,大幅提升国土空间规划和土地变更调查的工作效率。通过封装分类规则为Python函数并集成到ArcGIS Pro脚本工具中,解决了传统手动操作耗时且易错的问题,适用于大规模数据处理场景。

weixin_29081101的博客 304

堆的概念与运用

无

weixin_53136736的博客 158

1 FFmpeg从入门到精通-FFmpeg简介

1.FFmpeg简介 1.1 FFmpeg的定义 1.2 FFmpeg的历史 1.3 FFmpeg的基本组成 1.4 FFmpeg的编解码工具ffmpeg 1.5 FFmpeg的播放器ffplay 1.6 FFmpeg的多媒体分析器ffprobe 1.7 FFmpeg编译(1)FFmpeg之Windows平台编译(2)FFmpeg之Linux平台编译(3)FFmpeg之OS X平台编译 1.8 FFmpeg编码支持与定制(1)FFmpeg的编码器支持(2)FFmpe

migu123的专栏 3687

51雾化器实现

1. 雾化器简介 雾化器是将试液雾化的一种器械。雾化器是原子化系统的重要部件,其性能对测定的精密度和化学干扰等产生显著影响。因此要求雾化器喷雾稳定、雾滴细小、均匀和雾化效率高。雾化器的类型有超声雾化器,空气压缩式雾化器,网式雾化器。超声雾化器因其价格合适,适合家用,被广泛应用于加湿器,香薰机等各种小家电,也在医疗领域有很多的应用。家用雾化器的主要器件是一枚雾化片。 家用雾化器分为两种不同类别雾化片,实孔雾化片和微孔雾化片,这两种的喷雾方法和工作原理是有所不同的。不同的雾化片需要特定的频率才能工作。 实孔雾

qq_42415953的博客 2万+

对于堆的总结

一.堆 1.1 堆的概念 堆逻辑上是一棵完全二叉树。 堆物理上是保存在数组中。(使用数组保存二叉树结构,方式即将二叉树用层序遍历放入数组) 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆 。 反之,小堆,或者小根堆,或者最小堆 。 堆的基本作用是,快速找集合中的最值。 1.2堆的操作 --向下调整 前提:已经是一个堆了 说明: 1.array 代...

weixin_44936160的博客 374

Keil MDK-ARM v5.43a 部署 STM32F103 开发环境全流程

嵌入式开发环境是 Cortex-M 微控制器工程落地的基础支撑,其核心在于编译器链、设备支持包(DFP)与调试驱动的协同一致性。Keil MDK-ARM 作为工业级 ARM Cortex-M 开发套件,依托 Arm Compiler 和 CMSIS 标准,提供从代码编译、外设抽象到实时调试的全栈能力。其技术价值体现在对 STM32 等主流 MCU 的深度适配、高可靠性 SWD 调试支持,以及与 HAL 库、CubeMX 工程的无缝集成。典型应用场景涵盖温湿度传感终端、工业 PLC 子模块及低功耗边缘节点开发

weixin_33750664的博客 886

JVM学习笔记七--堆

文章目录一、堆的核心概述二、设置堆内存大小与OOM三、年轻代与老年代四、图解对象分配过程五、Minor GC、Major GC、Full GC六、堆空间分代思想七、内存分配策略八、为对象分配内存:TLAB九、小结堆空间的参数设置十、堆是分配对象的唯一选择吗? 一、堆的核心概述 二、设置堆内存大小与OOM 三、年轻代与老年代 四、图解对象分配过程 五、Minor GC、Major GC、Full GC 六、堆空间分代思想 七、内存分配策略 八、为对象分配内存:TLAB 九、小结堆空间的参数设置

weixin_45787495的博客 2387

PINN for PDE(偏微分方程)4 - 反向问题 - Burgers’ equation

PINN for PDE(偏微分方程)4 - 反向问题 - Burgers’ equation

m0_62030579的博客 3003

R语言基于机器学习三分类结果、计算三分类混淆矩阵、计算每一个类别对应的特异度(specificity)、灵敏度(sensitivity)、PPV、NPV

R语言基于机器学习三分类结果、计算三分类混淆矩阵、计算每一个类别对应的特异度(specificity)、灵敏度(sensitivity)、PPV、NPV

data+scenario+science+insight 648

JVM默认堆大小

官方文档: The default maximum heap size is half of the physical memory up to a physical memory size of 192 megabytes (MB) and otherwise one fourth of the physical memory up to a physical memory size of 1 ...

一位自然人的博客 1万+

YOLOv11改进,YOLOv11引入B2CNet的BFAM(双时相特征聚合模块),JSTARS 2024,二次创新C3k2结构

YOLOv11改进,YOLOv11引入B2CNet的BFAM(双时相特征聚合模块),JSTARS 2024,二次创新C3k2结构

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 406

JVM之 方法区 和 堆 的总结(学习笔记)

1、方法区(Method Area),又称永久代(Permanent Generation),又称非堆区(Non-Heap space) 方法区,又称永久代(Permanent Generation),常称为PermGen,位于非堆空间,又称非堆区(Non-Heap space)。 方法区是被所有线程共享。 所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。 简单说,所有定...

猎人在吃肉 6990

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

JVM中的堆和栈到底存储了什么

本文介绍了JVM运行时数据区的堆和栈内存结构。堆是线程共享的内存区域,主要用于存储对象实例和数组;栈是线程私有的运行单位,存储方法调用的局部变量和运行状态。两者主要区别在于:栈解决程序执行问题,堆解决数据存储问题;栈存储线程相关信息,堆存储对象信息。文章还分析了堆栈的内存分配机制和可能的内存溢出情况,并通过代码示例演示了变量在堆栈中的存储与回收过程,帮助理解JVM内存管理机制。

liwenxiang629的博客 1万+

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

JVM学习笔记之堆

目录 背景 核心概述 设置堆大小、OOM问题 年轻代与老年代 对象分配过程 Minor GC、Major GC、Full GC 堆空间分代思想 内存分配策略 TLAB 堆参数设置小结 堆不是分配对象的唯一选择 结语 背景 整理一下这几天学习宋红康JVM上篇剩下部分的笔记,从堆到垃圾回收器 从堆开始,测试内容都是基于jdk8的 核心概述 进程内唯一,线程共享,但可以划分线程私有缓冲区(Thread Local Allocation Buffer, TLAB)。 堆在JVM启

四国棋客 774

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

快速了解JVM中的深堆与浅堆

深堆与浅堆是JVM内存管理中不可或缺的概念。浅堆反映了对象自身的“基础重量”,体现了对象的类结构设计;而深堆则展示了对象的“影响力范围”,决定了对象是否能被GC回收。在实际的开发和调优过程中,当遇到内存问题时,我们可以按照以下步骤进行排查:首先使用jmap或jcmd生成Heap Dump文件;然后利用MAT分析深堆大的对象及其引用链;最后检查GC Roots,找出不必要的强引用关系。掌握深堆与浅堆的知识,将为我们优化Java程序的内存使用提供有力的支持。

llwszx的博客 1736

jvm学习笔记

说到jvm,就很容易想到jdk,jre,jvm,众所周知,jre是java运行环境,jdk是java开发工具,而jvm是java虚拟机,是运行java应用,可以跨平台的。对象头mark word:存储对象运行时的数据,比如所类型,对象年龄代,hash值,线程持有的锁,线程id等。数组长度:如果对象不是数组类型的话,则没有这部分。指针类型:指向元空间类信息的指针,jvm通过它来确定该对象时哪个类的实例。实例数据就是对象里的字段和存储的值,包括从父类继承的字段和自身的字段对齐填充。

yuayu123的博客 1757

JVM学习笔记——堆(Heap)

在上一篇文章中说到了什么是栈,栈内存中是连续的空间,保存一个个的栈帧,对应一次次方法的调用;还讲到了他是保存对象的引用,那么对象存在哪里呢?我们来看看昨天的一张图: 对象就存在图中的内存区域,在JVM中,那片区域叫做堆! 由图中可以看到堆的存储结构和栈是不同的,堆在内存中并不是一块连续的区域,他是分散的(物理上是分散,但逻辑上是连续的,大家好好体会一下);虚拟机通过栈中引用的指引在堆中找到所需...

程铭程铭你快成名的博客 1752

JVM中堆的简介

一:基本概念 Java 中的堆是 JVM 管理的最大的一块内存空间,主要用于存放Java类的实例对象,其被划分为两个不同的区域:新生代 ( Young )和老年代 ( Old ),其中新生代 ( Young )又被划分为:Eden、From Survivor和To Survivor三个区域,如下图所示: jdk7及以前版本 jdk8及以后版本 二:堆的各个区域 堆大小 = 新生代( Young...

wang_da_bing的博客 854

一篇了解JVM——堆Heap学习笔记

一、核心概述 通过new关键字创建的对象都会使用堆内存 它是线程共享的,堆中对象都需要考虑线程安全问题 堆,是GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域 《Java虚拟机规范》中对java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上。 从实际使用的角度看,“几乎”所有的对象的实例都在这里分配内存 (‘几乎’是因为可能存储在栈上) 数组或对象永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置 在方法结束...

qq_46037465的博客 858

JVM堆介绍

今天,我们将深入探索Java堆的奥秘。它是Java虚拟机中一个非常关键的内存区域。让我们一起揭开Java堆的神秘面纱吧!

guangzhi0633的专栏 733

JVM:堆、方法区

JVM:堆、方法区

weixin_74019079的博客 1257

JVM基础:深入学习JVM堆与JVM栈

个人博客导航页(点击右侧链接即可打开个人博客):大牛带你入门技术栈 以前堆是干啥栈是干啥都知道,就是没连在一起想想。感觉讲的不错的一篇儿~~JVM栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;JVM堆解决的是数据存储的问题,即数据怎么放、放在哪儿,另外JVM堆中存的是对象。JVM栈中存的是基本数据类型和JVM堆中对象的引用。 JVM基础概念:JVM堆与JVM栈 数据类型 ...

JKX_geek的博客 576

堆---JVM(七)

特殊过程 三个重要的GC

Eliza白的博客 309
上一篇: JVM学习笔记六:本地方法接口和本地方法栈
下一篇: JVM学习笔记八:方法区
种花途。
博客等级 码龄10年 3粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值