JVM--内存结构

JVM内存结构图解 一 真实系统中的概念   JVM(Java Virtual Machine),顾名思义是对真实计算机系统的模拟,正因如此才能屏蔽物理机器的变化,从而实现“一次编译,到处运行”。   相信很多Java程序员经常听到堆、栈等概念,也会进行设置调优以让Java应用能够更好地运行,但对于JVM与真实计算机系统之间的关系并没有特别清晰的认识。因此,这里先简单介绍下真实计算机系统中的一 阅读详情

1. 前言
Java 虚拟机是中、高级开发人员必须修炼的知识,有着较高的学习门槛,很多人都不情愿去接触它。可能是觉得学习成本较高又或者是感觉没什么实用性,所以干脆懒得“搭理”它了。其实这种想法是错误的。举个最简单的例子,JVM 基本上是每家招聘公司都会问到的问题,它们会这么无聊问这些不切实际的问题吗?很显然不是。由 JVM 引发的故障问题,无论在我们开发过程中还是生产环境下都是非常常见的。比如 OutOfMemoryError(OOM) 内存溢出问题,你应该遇到过 Tomcat 容器中加载项目过多导致的 OOM 问题,导致 Web 项目无法启动。这就是JVM引发的故障问题。那到底JVM哪里发生内存溢出了呢?为什么会内存溢出呢?如何监控?最重要的就是如何解决问题呢?能解决问题的技术才是最实用最好的技术。然而你对JVM的内存结构都不清楚,就妄想解决JVM引发的故障问题,是不切实际的。只有基础打好了,对于JVM故障问题才能“披荆斩棘”。本文通过代码与图示详细讲解了JVM内存区域,相信阅读本文之后,你将对JVM内存的堆、栈、方法区等有一个清晰的认知。

2. 运行时数据区
Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分为若干个不同的数据区域。每个区域都有各自的作用。

分析 JVM 内存结构,主要就是分析 JVM 运行时数据存储区域。JVM 的运行时数据区主要包括:堆、栈、方法区、程序计数器等。而 JVM 的优化问题主要在线程共享的数据区中:堆、方法区。

2.1 程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,可以看作是当前线程所执行字节码的行号指示器,指向下一个将要执行的指令代码,由执行引擎来读取下一条指令。更确切的说,一个线程的执行,是通过字节码解释器改变当前线程的计数器的值,来获取下一条需要执行的字节码指令,从而确保线程的正确执行。

为了确保线程切换后(上下文切换)能恢复到正确的执行位置,每个线程都有一个独立的程序计数器,各个线程的计数器互不影响,独立存储。也就是说程序计数器是线程私有的内存。

如果线程执行 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是 Native 方法,计数器值为Undefined。

程序计数器不会发生内存溢出(OutOfMemoryError即OOM)问题。

2.2 栈
JVM 中的栈包括 Java 虚拟机栈和本地方法栈,两者的区别就是,Java 虚拟机栈为 JVM 执行 Java 方法服务,本地方法栈则为 JVM 使用到的 Native 方法服务。两者作用是极其相似的,本文主要介绍 Java 虚拟机栈,以下简称栈。

Native 方法是什么?

JDK 中有很多方法是使用 Native 修饰的。Native 方法不是以 Java 语言实现的,而是以本地语言实现的(比如 C 或 C++)。个人理解Native 方法是与操作系统直接交互的。比如通知垃圾收集器进行垃圾回收的代码 System.gc(),就是使用 native 修饰的。

public final class System {
    public static void gc() {
        Runtime.getRuntime().gc();
    }
}
 
public class Runtime {
    //使用native修饰
     public native void gc();
什么是栈?

定义:限定仅在表头进行插入和删除操作的线性表。即压栈(入栈)和弹栈(出栈)都是对栈顶元素进行操作的。所以栈是后进先出的。

栈是线程私有的,他的生命周期与线程相同。每个线程都会分配一个栈的空间,即每个线程拥有独立的栈空间。

栈中存储的是什么?

栈帧是栈的元素。每个方法在执行时都会创建一个栈帧。栈帧中存储了局部变量表、操作数栈、动态连接和方法出口等信息。每个方法从调用到运行结束的过程,就对应着一个栈帧在栈中压栈到出栈的过程。

2.2.1 局部变量表
栈帧中,由一个局部变量表存储数据。局部变量表中存储了基本数据类型(boolean、byte、char、short、int、float、long、double)的局部变量(包括参数)、和对象的引用(String、数组、对象等),但是不存储对象的内容。局部变量表所需的内存空间在编译期间完成分配,在方法运行期间不会改变局部变量表的大小。

局部变量的容量以变量槽(Variable Slot)为最小单位,每个变量槽最大存储32位的数据类型。对于64位的数据类型(long、double),JVM 会为其分配两个连续的变量槽来存储。以下简称 Slot 。

JVM 通过索引定位的方式使用局部变量表,索引的范围从0开始至局部变量表中最大的 Slot 数量。普通方法与 static 方法在第 0 个槽位的存储有所不同。非 static 方法的第 0 个槽位存储方法所属对象实例的引用。

Slot 复用?

为了尽可能的节省栈帧空间,局部变量表中的 Slot 是可以复用的。方法中定义的局部变量,其作用域不一定会覆盖整个方法。当方法运行时,如果已经超出了某个变量的作用域,即变量失效了,那这个变量对应的 Slot 就可以交给其他变量使用,也就是所谓的 Slot 复用。通过一个例子来理解变量“失效”。

public void test(boolean flag)
{
    if(flag)
    {
        int a = 66;
    }
    
    int b = 55;
}
当虚拟机运行 test 方法,就会创建一个栈帧,并压入到当前线程的栈中。当运行到 int a = 66时,在当前栈帧的局部变量中创建一个 Slot 存储变量 a,当运行到 int b = 55时,此时已经超出变量 a 的作用域了(变量 a 的作用域在{}所包含的代码块中),此时 a 就失效了,变量a 占用的 Slot 就可以交给b来使用,这就是 Slot 复用。

凡事有利弊。Slot 复用虽然节省了栈帧空间,但是会伴随一些额外的副作用。比如,Slot 的复用会直接影响到系统的垃圾收集行为。

public class TestDemo {
 
    public static void main(String[] args){
        
        byte[] placeholder = new byte[64 * 1024 * 1024];
        
        System.gc();
    }
}
上段代码很简单,先向内存中填充了 64M 的数据,然后通知虚拟机进行垃圾回收。为了更清晰的查看垃圾回收的过程,我们再虚拟机的运行参数中加上“-verbose:gc”,这个参数的作用就是打印 GC 信息。

打印的GC信息如下:

可以看到虚拟机没有回收这 64M 内存。为什么没有被回收?其实很好理解,当执行 System.gc() 方法时,变量 placeholder 还在作用域范围之内,虚拟机是不会回收的,它还是“有效”的。

我们对上面的代码稍作修改,使其作用域“失效”。

 

public class TestDemo {
 
    public static void main(String[] args){
        {
            byte[] placeholder = new byte[64 * 1024 * 1024];
        }
        System.gc();
    }
}
当运行到 System.gc() 方法时,变量 placeholder 的作用域已经失效了。它已经“无用”了,虚拟机会回收它所占用的内存了吧?

运行结果:

发现虚拟机还是没有回收 placeholder 变量占用的 64M 内存。为什么所想非所见呢?在解释之前,我们再对代码稍作修改。在System.gc()方法执行之前,加入一个局部变量。

 

public class TestDemo {
 
    public static void main(String[] args){
        {
            byte[] placeholder = new byte[64 * 1024 * 1024];
        }
        int a = 0;
        System.gc();
    }
}
在 System.gc() 方法之前,加入 int a = 0,再执行方法,查看垃圾回收情况。

发现 placeholder 变量占用的64M内存空间被回收了,如果不理解局部变量表的Slot复用,很难理解这种现象的。

而 placeholder 变量能否被回收的关键就在于:局部变量表中的 Slot 是否还存有关于 placeholder 对象的引用。

第一次修改中,限定了 placeholder 的作用域,但之后并没有任何对局部变量表的读写操作,placeholder 变量在局部变量表中占用的Slot没有被其它变量所复用,所以作为 GC Roots 一部分的局部变量表仍然保持着对它的关联。所以 placeholder 变量没有被回收。

第二次修改后,运行到 int a = 0 时,已经超过了 placeholder 变量的作用域,此时 placeholder 在局部变量表中占用的Slot可以交给其他变量使用。而变量a正好复用了 placeholder 占用的 Slot,至此局部变量表中的 Slot 已经没有 placeholder 的引用了,虚拟机就回收了placeholder 占用的 64M 内存空间。

2.2.2 操作数栈
操作数栈是一个后进先出栈。操作数栈的元素可以是任意的Java数据类型。方法刚开始执行时,操作数栈是空的,在方法执行过程中,通过字节码指令对操作数栈进行压栈和出栈的操作。通常进行算数运算的时候是通过操作数栈来进行的,又或者是在调用其他方法的时候通过操作数栈进行参数传递。操作数栈可以理解为栈帧中用于计算的临时数据存储区。

通过一段代码来了解操作数栈。

public class OperandStack{
 
    public static int add(int a, int b){
        int c = a + b;
        return c;
    }
 
    public static void main(String[] args){
        add(100, 98);
    }
}
使用 javap 反编译 OperandStack 后,根据虚拟机指令集,得出操作数栈的运行流程如下:

add 方法刚开始执行时,操作数栈是空的。当执行 iload_0 时,把局部变量 0 压栈,即 100 入操作数栈。然后执行 iload_1,把局部变量1压栈,即 98 入操作数栈。接着执行 iadd,弹出两个变量(100 和 98 出操作数栈),对 100 和 98 进行求和,然后将结果 198 压栈。然后执行 istore_2,弹出结果(出栈)。

下面通过一张图,对比执行100+98操作,局部变量表和操作数栈的变化情况。

栈中可能出现哪些异常?

StackOverflowError:栈溢出错误

如果一个线程在计算时所需要用到栈大小 > 配置允许最大的栈大小,那么Java虚拟机将抛出 StackOverflowError

OutOfMemoryError:内存不足

 栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。

如何设置栈参数?

使用 -Xss 设置栈大小,通常几百K就够用了。由于栈是线程私有的,线程数越多,占用栈空间越大。

栈决定了函数调用的深度。这也是慎用递归调用的原因。递归调用时,每次调用方法都会创建栈帧并压栈。当调用一定次数之后,所需栈的大小已经超过了虚拟机运行配置的最大栈参数,就会抛出 StackOverflowError 异常。

2.3 Java堆
堆是Java虚拟机所管理的内存中最大的一块存储区域。堆内存被所有线程共享。主要存放使用new关键字创建的对象。所有对象实例以及数组都要在堆上分配。垃圾收集器就是根据GC算法,收集堆上对象所占用的内存空间(收集的是对象占用的空间而不是对象本身)。

Java堆分为年轻代(Young Generation)和老年代(Old Generation);年轻代又分为伊甸园(Eden)和幸存区(Survivor区);幸存区又分为From Survivor空间和 To Survivor空间。

年轻代存储“新生对象”,我们新创建的对象存储在年轻代中。当年轻内存占满后,会触发Minor GC,清理年轻代内存空间。

老年代存储长期存活的对象和大对象。年轻代中存储的对象,经过多次GC后仍然存活的对象会移动到老年代中进行存储。老年代空间占满后,会触发Full GC。

注:Full GC是清理整个堆空间,包括年轻代和老年代。如果Full GC之后,堆中仍然无法存储对象,就会抛出OutOfMemoryError异常。

Java堆设置常用参数

参数    描述
-Xms    堆内存初始大小
-Xmx(MaxHeapSize)    堆内存最大允许大小,一般不要大于物理内存的80%
-XX:NewSize(-Xns)    年轻代内存初始大小
-XX:MaxNewSize(-Xmn)    年轻代内存最大允许大小,也可以缩写
-XX:NewRatio

新生代和老年代的比值

值为4 表示 新生代:老年代=1:4,即年轻代占堆的1/5

-XX:SurvivorRatio=8    
年轻代中Eden区与Survivor区的容量比例值,默认为8

表示两个Survivor :eden=2:8,即一个Survivor占年轻代的1/10

-XX:+HeapDumpOnOutOfMemoryError

内存溢出时,导出堆信息到文件

-XX:+HeapDumpPath

堆Dump路径

-Xmx20m -Xms5m

-XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=d:/a.dump

-XX:OnOutOfMemoryError

当发生OOM内存溢出时,执行一个脚本

-XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p

%p表示线程的id pid

-XX:MaxTenuringThreshold=7    表示如果在幸存区移动多少次没有被垃圾回收,进入老年代
2.4 方法区(Method Area)
方法区同 Java 堆一样是被所有线程共享的区间,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码。更具体的说,静态变量+常量+类信息(版本、方法、字段等)+运行时常量池存在方法区中。常量池是方法区的一部分。

注:JDK1.8 使用元空间 MetaSpace 替代方法区,元空间并不在 JVM中,而是使用本地内存。元空间两个参数:

 MetaSpaceSize:初始化元空间大小,控制发生GC阈值
 MaxMetaspaceSize : 限制元空间大小上限,防止异常占用过多物理内存
 

常量池中存储编译器生成的各种字面量和符号引用。字面量就是Java中常量的意思。比如文本字符串,final修饰的常量等。方法引用则包括类和接口的全限定名,方法名和描述符,字段名和描述符等。

常量池有什么用 ?

优点:常量池避免了频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。

举个栗子: Integer 常量池(缓存池),和字符串常量池

Integer常量池:

我们知道 == 基本数据类型比较的是数值,而引用数据类型比较的是内存地址。

public void TestIntegerCache()
{
    public static void main(String[] args)
    {
        
        Integer i1 = new Integer(66);
        Integer i2 = new integer(66);
        Integer i3 = 66;
        Integer i4 = 66;
        Integer i5 = 150;
        Integer i6 = 150;
        System.out.println(i1 == i2);//false
        System.out.println(i3 == i4);//true
        System.out.println(i5 == i6);//false
    }
    
}
i1 和 i2 使用 new 关键字,每 new 一次都会在堆上创建一个对象,所以 i1 == i2 为 false。

i3 == i4 为什么是 true 呢?Integer i3 = 66 实际上有一步装箱的操作,即将 int 型的 66 装箱成 Integer,通过 Integer 的 valueOf 方法。

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
Integer 的 valueOf 方法很简单,它判断变量是否在 IntegerCache 的最小值(-128)和最大值(127)之间,如果在,则返回常量池中的内容,否则 new 一个 Integer 对象。

而 IntegerCache 是 Integer的静态内部类,作用就是将 [-128,127] 之间的数“缓存”在 IntegerCache 类的 cache 数组中,valueOf 方法就是调用常量池的 cache 数组,不过是将 i3、i4 变量引用指向常量池中,没有真正的创建对象。而new Integer(i)则是直接在堆中创建对象。

IntegerCache 类中,包含一个构造方法,三个静态变量:low最小值、high最大值、和Integer数组,还有一个静态代码块。静态代码块的作用就是在 IntegerCache 类加载的时候,对high最大值以及 Integer 数组初始化。也就是说当 IntegerCache 类加载的时候,最大最小值,和 Integer 数组就已经初始化好了。这个 Integer 数组其实就是包含了 -128到127之间的所有值。

IntegerCache 源码

private static class IntegerCache {
        static final int low = -128;//最小值
        static final int high;//最大值
        static final Integer cache[];//缓存数组
 
        //私有化构造方法,不让别人创建它。单例模式的思想
        private IntegerCache() {}
 
        //类加载的时候,执行静态代码块。作用是将-128到127之间的数缓冲在cache[]数组中
        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
 
            cache = new Integer[(high - low) + 1];//初始化cache数组,根据最大最小值确定
            int j = low;
            for(int k = 0; k < cache.length; k++)//遍历将数据放入cache数组中
                cache[k] = new Integer(j++);
 
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }
 
    }
而 i5 == i6 为 false,就是因为 150 不在 Integer 常量池的最大最小值之间【-128,127】,从而 new 了一个对象,所以为 false。

再看一段拆箱的代码。

public static void main(String[] args){
       Integer i1 = new Integer(4);
       Integer i2 = new Integer(6);
       Integer i3 = new Integer(10);
       System.out.print(i3 == i1+i2);//true
    }
由于 i1 和 i2 是 Integer 对象,是不能使用+运算符的。首先 i1 和 i2 进行自动拆箱操作,拆箱成int后再进行数值加法运算。i3 也是拆箱后再与之比较数值是否相等的。所以 i3 == i1+i2 其实是比较的 int 型数值是否相等,所以为true。

String常量池:

String 是由 final 修饰的类,是不可以被继承的。通常有两种方式来创建对象。

//1、
String str = new String("abcd");
 
//2、
String str = "abcd";
第一种使用 new 创建的对象,存放在堆中。每次调用都会创建一个新的对象。

第二种先在栈上创建一个 String 类的对象引用变量 str,然后通过符号引用去字符串常量池中找有没有 “abcd”,如果没有,则将“abcd”存放到字符串常量池中,并将栈上的 str 变量引用指向常量池中的“abcd”。如果常量池中已经有“abcd”了,则不会再常量池中创建“abcd”,而是直接将 str 引用指向常量池中的“abcd”。

对于 String 类,equals 方法用于比较字符串内容是否相同; == 号用于比较内存地址是否相同,即是否指向同一个对象。通过代码验证上面理论。

public static void main(String[] args){
       String str1 = "abcd";
       String str2 = "abcd";
       System.out.print(str1 == str2);//true
    }
首先在栈上存放变量引用 str1,然后通过符号引用去常量池中找是否有 abcd,没有,则将 abcd 存储在常量池中,然后将 str1 指向常量池的 abcd。当创建 str2 对象,去常量池中发现已经有 abcd 了,就将 str2 引用直接指向 abcd 。所以str1 == str2,指向同一个内存地址。

public static void main(String[] args){
       String str1 = new String("abcd");
       String str2 = new String("abcd");
       System.out.print(str1 == str2);//false
    }
str1 和 str2 使用 new 创建对象,分别在堆上创建了不同的对象。两个引用指向堆中两个不同的对象,所以为 false。

关于字符串 + 号连接问题:

对于字符串常量的 + 号连接,在程序编译期,JVM就会将其优化为 + 号连接后的值。所以在编译期其字符串常量的值就确定了。

String a = "a1";   
String b = "a" + 1;   
System.out.println((a == b)); //result = true  
 
String a = "atrue";   
String b = "a" + "true";   
System.out.println((a == b)); //result = true 
 
String a = "a3.4";   
String b = "a" + 3.4;   
System.out.println((a == b)); //result = true 
关于字符串引用 + 号连接问题:

对于字符串引用的 + 号连接问题,由于字符串引用在编译期是无法确定下来的,在程序的运行期动态分配并创建新的地址存储对象。

public static void main(String[] args){
       String str1 = "a";
       String str2 = "ab";
       String str3 = str1 + "b";
       System.out.print(str2 == str3);//false
    }
对于上边代码,str3 等于 str1 引用 + 字符串常量“b”,在编译期无法确定,在运行期动态的分配并将连接后的新地址赋给 str3,所以 str2 和 str3 引用的内存地址不同,所以 str2 == str3 结果为 false

通过 jad 反编译工具,分析上述代码到底做了什么。编译指令如下:

经过 jad 反编译工具反编译代码后,代码如下

public class TestDemo
{
 
    public TestDemo()
    {
    }
 
    public static void main(String args[])
    {
        String s = "a";
        String s1 = "ab";
        String s2 = (new StringBuilder()).append(s).append("b").toString();
        System.out.print(s1 = s2);
    }
}
发现 new 了一个 StringBuilder 对象,然后使用 append 方法优化了 + 操作符。new 在堆上创建对象,而 String s1=“ab”则是在常量池中创建对象,两个应用所指向的内存地址是不同的,所以 s1 == s2 结果为 false。

注:我们已经知道了字符串引用的 + 号连接问题,其实是在运行期间创建一个 StringBuilder 对象,使用其 append 方法将字符串连接起来。这个也是我们开发中需要注意的一个问题,就是尽量不要在 for 循环中使用 + 号来操作字符串。看下面一段代码:

public static void main(String[] args){
        String s = null;
        for(int i = 0; i < 100; i++){
            s = s + "a";
        }
    }
在 for 循环中使用 + 连接字符串,每循环一次,就会新建 StringBuilder 对象,append 后就“抛弃”了它。如果我们在循环外创建StringBuilder 对象,然后在循环中使用 append 方法追加字符串,就可以节省 n-1 次创建和销毁对象的时间。所以在循环中连接字符串,一般使用 StringBuilder 或者 StringBuffer,而不是使用 + 号操作。

public static void main(String[] args){
        StringBuilder s = new StringBuilder();
        for(int i = 0; i < 100; i++){
            s.append("a");
        }
    }
使用final修饰的字符串

public static void main(String[] args){
        final String str1 = "a";
        String str2 = "ab";
        String str3 = str1 + "b";
        System.out.print(str2 == str3);//true
    }
final 修饰的变量是一个常量,编译期就能确定其值。所以 str1 + "b"就等同于 "a" + "b",所以结果是 true。

String对象的intern方法。

public static void main(String[] args){
        String s = "ab";
        String s1 = "a";
        String s2 = "b";
        String s3 = s1 + s2;
        System.out.println(s3 == s);//false
        System.out.println(s3.intern() == s);//true
    }
通过前面学习我们知道,s1+s2 实际上在堆上 new 了一个 StringBuilder 对象,而 s 在常量池中创建对象 “ab”,所以 s3 == s 为 false。但是 s3 调用 intern 方法,返回的是s3的内容(ab)在常量池中的地址值。所以 s3.intern() == s 结果为 true。
 

JVM-01 JVM内存结构 文章目录1.JVM整体架构1.1 JVM(虚拟机)1.2 Java虚拟机阵营2.JVM子系统构成3.JVM内存结构3.1 名词解释3.2 JVM对该区域规范了两种异常3.3 实例详解Java栈3.4 栈+堆+方法区的交互关系3.5 堆(线程共享)3.5.1 **为什么jdk1.8用元数据区取代了永久代**?3.5.2 java虚拟机调优的目的3.6 JVM执行案例分析3.6.1 编写java类3.... 阅读详情

相关推荐

JVM内存结构

JVM的内存结构大概分为: 堆(Heap):线程共享。所有的对象实例以及数组都要在堆上分配。回收器主要管理的对象。 方法区(Method Area):线程共享。存储类信息、常量、静态变量、即时编译器编译后的代码。 方法栈(JVM Stack):线程私有。存储局部变量表、操作栈、动态链接、方法出口,对象指针。 本地方法栈(Native Method Stack):线程私有。为虚拟机使用到的Native 方法服务。如Java使用c或者c++编写的接口服务时,代码在此区运行。 程序计数器(Program Co.

阿杰同学的博客 1770

JVM之内存结构详解

对于开发人员来说,如果不了解Java的JVM,那真的是很难写得一手好代码,很难查得一手好bug。同时,JVM也是面试环节的中重灾区。今天开始,《JVM详解》系列开启,带大家深入了解JVM相关知识。 我们不能为了面试而面试,但是学习会这些核心知识你必定会成为面试与工作中“最亮的一颗星”。本系列首发于微信公众号“程序新视界”。下面,开启我们的第一篇文章《JVM之内存结构详解》。 学习也是要讲究方式方法...

程序新视界 3万+

STM32F030C8烧录实战:用J-Link Commander实现一键批量烧写(附完整脚本)

本文详细介绍了如何使用J-Link Commander工具链实现STM32F030C8的一键批量烧录,包括环境配置、批处理脚本设计、命令脚本解析以及生产环境实战技巧。通过自动化烧录流程,显著提升效率并降低人为错误,特别适合量产和频繁迭代的开发场景。

weixin_30505225的博客 459

JVM内存结构概述

本节将会介绍一下JVM的内存结构,JVM运行时数据区的各个组成部分:堆,方法区,程序计数器,Java虚拟机栈,本地方法栈,还会对Java堆的分代划分做个简单的介绍。 目录 前言 JVM是什么 JVM内存结构概览 运行时数据区 程序计数器 Java虚拟机栈 本地方法栈 方法区 运行时常量池 Java堆 直接内存 前言 JVM是Java中比较难理解和掌握的一部分,也是面试...

没头脑遇到不高兴 5026

实施文档

流程管理 2008-04-23 09:44:00 874 收藏 3 分类专栏: 企业管理杂谈 文章标签: 文档 文档管理 项目管理 工作 配置管理 工具 项目实施文档是用来记录、描述、展示实施过程中一系列信息的处理过程,通过书面或图示的形式对项目活动过程或结果进行描述、定义、规定及报告。例如分阶段实施计划文档,标准业务流程文档,标准编码及数据文档,标准参数设置文档,功能操作指南文档。这些文档将会伴随着ERP实施的各个阶段逐渐充实、完善,也同时记载了整个实施的过程和成果。 作为实施顾问人员,在实施过程

qq_52337172的博客 1062

干货——Joplin私人笔记安装部署

安全、隐私、免费、不限制的私人笔记的选择和搭建

vincentho1024的专栏 7876

JVM 内存结构

本文主要对JVM 内存结构进行讲解,注意不要和Java内存模型混淆了。通过下面两个问题,理解下PC计数器使用PC寄存器存储字节码指令地址有什么用呢?为什么使用PC寄存器记录当前线程的执行地址呢?因为CPU需要不停的切换各个线程,这时候切换回来以后,就得知道接着从哪开始继续执行。JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令。PC寄存器为什么会被设定为线程私有的?

埃泽漫笔 1492

ROS2实战:打造智能巡检机器人的关键技术与实现

本文详细介绍了基于ROS2和Navigation2构建智能巡检机器人的完整实战流程。通过设计主从协作的事件驱动架构,实现了机器人在已知地图中的自动导航、定点语音播报和环境图像采集功能。文章涵盖了从环境搭建、仿真配置到核心控制节点、语音服务与图像记录模块的代码实现,为开发者打造实用的自动巡检机器人提供了清晰的技术指南。

weixin_29031057的博客 222

jvm内存结构_聊聊JVM内存结构

起因我们经常会在面试的时候被问到JVM的内存结构,很多人会觉得这东西真的有用吗?也就是面试造火箭,入职拧螺丝。问这个就是纯粹来刁难人的吧。但实际上,我们细想一下。•假设你不知道局部变量实际上属于线程栈中的而非堆里面的,那么你就可能会担心是不是我这样写会有并发问题;•假设你不知道对象实例是分配在堆中,属于JVM共用的,那么你根本也就不会考虑在进行某些访问时进行加锁;•还有很多高并发的问题,...

weixin_39645343的博客 285

Aruco标签定位、测距、三维位置估算 opencv应用

摄像头要一开始对准aruco标签才开始运行。+ 相机标定文件(参考我的上篇文章)+ aruco标签6*6。

dgut_guangdian的博客 4221

海康威视嵌入式最全面试题及参考答案(3万字长文)

主营业务:提供包括前端摄像头、后端存储设备、显示控制设备等在内的全方位视频监控解决方案。市场地位:在全球范围内享有较高的市场份额,在安防领域具有较强的竞争力。技术创新:持续投入研发,拥有多项核心技术和专利,特别是在视频处理算法、人工智能等方面取得了显著成果。社会责任:积极参与公共安全项目,为智慧城市、交通管理等领域提供技术支持和服务。国际业务:除了国内市场外,海康威视的产品和服务也销往全球多个国家和地区。面向对象编程。

大模型大数据攻城狮的专栏 1763

JVM内存结构详解

堆是JVM中最大的一块内存区域,用于存放对象实例和数组,是垃圾收集器管理的主要区域。

cuishujian_2003的博客 1000

自动驾驶算法-滤波器系列(三)——不同运动模型(CV、CA、CTRV、CTRA)的建模和推导

CV & CA & CTRV & CTRA1. CV模型2. CA模型3. CTRV模型4. CTRA模型 1. CV模型 2. CA模型 3. CTRV模型 4. CTRA模型

weixin_41480034的博客 2万+

jvm 内存结构

上面说到,jdk1.8 中,已经不存在永久代(方法区),替代它的一块空间叫做 “ 元空间 ”,和永久代类似,都是 JVM 规范对方法区的实现,但是元空间并不在虚拟机中,而是使用本地内存,元空间的大小仅受本地内存限制,但可以通过 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 来指定元空间的大小。因此,可以这么说,程序计数器是每个线程所私有的。由于在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的,因此,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令,

weixin_62008675的博客 1376

仿真系列专栏介绍

大家好!欢迎来到我的仿真学习系列专栏!这是一个我在学习仿真技术过程中整理和分享的系列文章。作为一名正在不断学习和实践的工程师,我希望通过这个专栏,和大家一起探索仿真技术,尤其是在高速信号完整性方面的应用。

案例文章,高速信号 367

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

jvm内存结构

jvm内存结构

Zuo_Ci的博客 1375

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

JVM 内存结构 详解

JVM 内存结构 详解

T_Y_F_的博客 951

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

JVM详解——内存结构

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,具体实现有很多,以下内容如果不额外声明,默认是HotSpot JVM。JVM它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

eyes++的博客 4248

[JVM}——内存结构

本文介绍了jvm的内存结构,通过图解的方式让你可以简单易懂

热爱编程的机械生的救赎 1462

【Java】JVM内存结构

JVM 是一种虚拟计算机,负责加载、验证、解释/编译和执行 Java 字节码,并管理程序的内存、线程、垃圾回收等运行细节。项目JVM 的作用运行环境加载和执行 Java 字节码平台无关一次编写,到处运行内存管理管理对象分配和垃圾回收安全高效字节码验证 + JIT 优化Java 内存模型(JMM)是并发编程的核心基础,确保多线程下数据共享时的原子性、可见性、有序性,通过volatilefinal等关键字落地实现。特性问题解决关键字 / 工具是否 CPU 保证原子性操作不可中断。

weixin_46589153的博客 788

jvm内存结构_JVM之内存结构详解

对于开发人员来说,如果不了解Java的JVM,那真的是很难写得一手好代码,很难查得一手好bug。同时,JVM也是面试环节的中重灾区。今天开始,《JVM详解》系列开启,带大家深入了解JVM相关知识。我们不能为了面试而面试,但是学习会这些核心知识你必定会成为面试与工作中“最亮的一颗星”。本系列首发于微信公众号“程序新视界”。下面,开启我们的第一篇文章《JVM之内存结构详解》。思考一下学习一项知识总该知...

weixin_39596835的博客 158

JVM的内存结构

JVM是按照是按照运行时数据的存储结构来划分内存结构的,JVM在运行java程序的时候,将它们划分成了几种不同的数据结构,分别存储在不同的区域。 JVM的虚拟规范中,划分成6种, 1、pc计数器: pc计数器,也叫程序计数器,是用来保存当前正常执行的程序的内存地址。由于线程是cpu调用的最小单元,java程序是多线程执行的。也就是说PC计数器需要记下每条线程当前运行的虚拟机字节码指令地址。当然如果...

奋斗 551
上一篇: JVM--垃圾回收器(终结篇)
下一篇: docker入门
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值