精华内容
下载资源
问答
  • JVM内存分配策略 关于JVM的内存结构及内存分配方式,不是本文的重点,这里只做简单回顾。以下是我们知道的一些常识: 1、根据Java虚拟机规范,Java虚拟机所管理的内存包括方法区、虚拟机栈、本地方法栈、、程序...

    JVM内存分配策略

    关于JVM的内存结构及内存分配方式,不是本文的重点,这里只做简单回顾。以下是我们知道的一些常识:

    1、根据Java虚拟机规范,Java虚拟机所管理的内存包括方法区、虚拟机栈、本地方法栈、堆、程序计数器等。

    2、我们通常认为JVM中运行时数据存储包括堆和栈。这里所提到的栈其实指的是虚拟机栈,或者说是虚拟栈中的局部变量表。

    3、栈中存放一些基本类型的变量数据(int/short/long/byte/float/double/Boolean/char)和对象引用。

    4、堆中主要存放对象,即通过new关键字创建的对象。

    5、数组引用变量是存放在栈内存中,数组元素是存放在堆内存中。

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

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

    这里只是简单提了一句,并没有深入分析,很多人看到这里由于对JIT、逃逸分析等技术不了解,所以也无法真正理解上面这段话的含义。

    PS:这里默认大家都了解什么是JIT,不了解的朋友可以先自行Google了解下

    其实,在编译期间,JIT会对代码做很多优化。其中有一部分优化的目的就是减少内存堆分配压力,其中一种重要的技术叫做逃逸分析

    逃逸分析

    逃逸分析(Escape Analysis)是目前Java虚拟机中比较前沿的优化技术。这是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。

    逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中,称为方法逃逸。

    例如:

    public static StringBuffer craeteStringBuffer(String s1, String s2) {
       StringBuffer sb = new StringBuffer();
       sb.append(s1);
       sb.append(s2);
       return sb;
    }

    StringBuffer sb是一个方法内部变量,上述代码中直接将sb返回,这样这个StringBuffer有可能被其他方法所改变,这样它的作用域就不只是在方法内部,虽然它是一个局部变量,称其逃逸到了方法外部。甚至还有可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量,称为线程逃逸。

    上述代码如果想要StringBuffer sb不逃出方法,可以这样写:

    public static String createStringBuffer(String s1, String s2) {
       StringBuffer sb = new StringBuffer();
       sb.append(s1);
       sb.append(s2);
       return sb.toString();
    }

    不直接返回 StringBuffer,那么StringBuffer将不会逃逸出方法。

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

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

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

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

    上面的关于同步省略的内容,我在《深入理解多线程(五)—— Java虚拟机的锁优化技术》中有介绍过,即锁优化中的锁消除技术,依赖的也是逃逸分析技术。

    本文,主要来介绍逃逸分析的第二个用途:将堆分配转化为栈分配。

    其实,以上三种优化中,栈上内存分配其实是依靠标量替换来实现的。由于不是本文重点,这里就不展开介绍了。如果大家感兴趣,我后面专门出一篇文章,全面介绍下逃逸分析。

    在Java代码运行时,通过JVM参数可指定是否开启逃逸分析,

     -XX:+DoEscapeAnalysis : 表示开启逃逸分析

     -XX:-DoEscapeAnalysis : 表示关闭逃逸分析 

    从jdk 1.7开始已经默认开始逃逸分析,如需关闭,需要指定-XX:-DoEscapeAnalysis

    对象的栈上内存分配

    我们知道,在一般情况下,对象和数组元素的内存分配是在堆内存上进行的。但是随着JIT编译器的日渐成熟,很多优化使这种分配策略并不绝对。JIT编译器就可以在编译期间根据逃逸分析的结果,来决定是否可以将对象的内存分配从堆转化为栈。

    我们来看以下代码:

    public static void main(String[] args{
       long a1 = System.currentTimeMillis();
       for (int i = 0; i < 1000000; i++) {
           alloc();
       }
       // 查看执行时间
       long a2 = System.currentTimeMillis();
       System.out.println("cost " + (a2 - a1) + " ms");
       // 为了方便查看堆内存中对象个数,线程sleep
       try {
           Thread.sleep(100000);
       } catch (InterruptedException e1) {
           e1.printStackTrace();
       }
    }
    
    private static void alloc() {
       User user = new User();
    }
    
    static class User {
    
    }

    其实代码内容很简单,就是使用for循环,在代码中创建100万个User对象。

    我们在alloc方法中定义了User对象,但是并没有在方法外部引用他。也就是说,这个对象并不会逃逸到alloc外部。经过JIT的逃逸分析之后,就可以对其内存分配进行优化。

    我们指定以下JVM参数并运行:

    -Xmx4G -Xms4G -XX:-DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

    在程序打印出 cost XX ms 后,代码运行结束之前,我们使用[jmap][1]命令,来查看下当前堆内存中有多少个User对象:

    ➜  ~ jps
    2809 StackAllocTest
    2810 Jps
    ➜  ~ jmap -histo 2809
    
    num     #instances         #bytes  class name
    ----------------------------------------------
      1:           524       87282184  [I
      2:       1000000       16000000  StackAllocTest$User
      3:          6806        2093136  [B
      4:          8006        1320872  [C
      5:          4188         100512  java.lang.String
      6:           581          66304  java.lang.Class

    从上面的jmap执行结果中我们可以看到,堆中共创建了100万个StackAllocTest$User实例。

    在关闭逃避分析的情况下(-XX:-DoEscapeAnalysis),虽然在alloc方法中创建的User对象并没有逃逸到方法外部,但是还是被分配在堆内存中。也就说,如果没有JIT编译器优化,没有逃逸分析技术,正常情况下就应该是这样的。即所有对象都分配到堆内存中。

    接下来,我们开启逃逸分析,再来执行下以上代码。

    -Xmx4G -Xms4G -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError

    在程序打印出 cost XX ms 后,代码运行结束之前,我们使用jmap命令,来查看下当前堆内存中有多少个User对象:

    ➜  ~ jps
    709
    2858 Launcher
    2859 StackAllocTest
    2860 Jps
    ➜  ~ jmap -histo 2859
    
    num     #instances         #bytes  class name
    ----------------------------------------------
      1:           524      101944280  [I
      2:          6806        2093136  [B
      3:         83619        1337904  StackAllocTest$User
      4:          8006        1320872  [C
      5:          4188         100512  java.lang.String
      6:           581          66304  java.lang.Class

    从以上打印结果中可以发现,开启了逃逸分析之后(-XX:+DoEscapeAnalysis),在堆内存中只有8万多个StackAllocTest$User对象。也就是说在经过JIT优化之后,堆内存中分配的对象数量,从100万降到了8万。

    除了以上通过jmap验证对象个数的方法以外,读者还可以尝试将堆内存调小,然后执行以上代码,根据GC的次数来分析,也能发现,开启了逃逸分析之后,在运行期间,GC次数会明显减少。正是因为很多堆上分配被优化成了栈上分配,所以GC次数有了明显的减少。

    总结

    所以,如果以后再有人问你:是不是所有的对象和数组都会在堆内存分配空间?

    那么你可以告诉他:不一定,随着JIT编译器的发展,在编译期间,如果JIT经过逃逸分析,发现有些对象没有逃逸出方法,那么有可能堆内存分配会被优化成栈内存分配。但是这也并不是绝对的。就像我们前面看到的一样,在开启逃逸分析之后,也并不是所有User对象都没有在堆上分配。

    转自Hollis

    展开全文
  • 区:由程序员分配和释放,或者由系统释放,不连续分配,容易造成内存碎片 全局区:全局变量和静态变量的存储 文字常量区:常量,字符串 程序代码区,主要存储二进制文件 3.struct和class区别: s...

    1.程序占用的内存可以分为以下几种情况:
    栈区(stack)
    堆区(heap)
    全局区(static静态区)
    文字常量区
    程序区
    2.下面对内存分配进行总结
    栈区:存储函数参数,占据连续的存储空间
    堆区:由程序员分配和释放,或者由系统释放,不连续分配,容易造成内存碎片
    全局区:全局变量和静态变量的存储
    文字常量区:常量,字符串
    程序代码区,主要存储二进制文件
    3.struct和class区别:
    struct值类型:栈上分配内存
    class引用类型:堆上分配内容

    展开全文
  • 4、生长方向不同:生长方向是向上的,也就是向着内存增加的方向;栈相反。 5、分配方式不同:是动态的,没有静态的;栈有两种:动态和静态。 6、分配效率不同:栈,系统提供底层支持,有专门的寄存器存放栈...
    1、管理方式不同:栈由编译器管理;堆由程序员管理。
    
    2、空间大小不同:win32中,堆可达4G;VC中栈默认1M(可以修改)。
    3、碎片问题:堆易产生;栈不会。
    4、生长方向不同:堆生长方向是向上的,也就是向着内存增加的方向;栈相反。
    5、分配方式不同:堆是动态的,没有静态的堆;栈有两种:动态和静态。
    6、分配效率不同:栈,系统提供底层支持,有专门的寄存器存放栈地址,效率高;堆,由库函数提供支持,效率底。
    展开全文
  • C#new出来的结构体内存分配在堆上

    千次阅读 2018-09-11 22:46:00
    我的直觉是但凡使用new的东西都在堆上分配内存,除非C#对结构体做了特殊处理。 new int[10]这个说明不了什么,因为数组是引用类型,必然在堆上分配。 如何验证? 利用UNITY的profiler来观察内存变化 :new一个内存...

    C#new出来的结构体内存分配在堆上

    如题,有同事说因为结构体是值类型,所以 new出来的也是分配在栈上的。我的直觉是但凡使用new的东西都在堆上分配内存,除非C#对结构体做了特殊处理。

    new int[10]这个说明不了什么,因为数组是引用类型,必然在堆上分配。

    如何验证?

    利用UNITY的profiler来观察内存变化 :new 一个内存占用很大的结构体,比如80M,看GC ALLOC 突现了80M,说明确实是在堆上分配的。然后GC掉,多次重复现象相同。

     

            static void funcformemheaporstack()
            {
                //1, C#中的引用相当于C++中的智能指针,而不是原始指针, 原始指针无法进行引用计数,因而无法进行自动的内存回收
                ST001 ot = new ST001(); //ot相当于一个ST001类型的智能指针(对象),出函数后,对象被销毁,对堆的引用计数减1
                ot.x = 10;
                ST001 ot2 = ot; //值拷贝,而不是地址拷贝
                ot2.x = 20;
                Console.Write(ot.x); //10
    
                //2,结构体类型对象可分配在堆上,也可分配在栈上
                ST001 ot3; //栈上(class不能这样用)
                ot3.x = 10; ot3.y = 20; ot3.z = 30;
    
                STWrap ow;
                //ow.x = 10; //error,ow没有初始化
                STWrap ow2 = new STWrap();
                ow2.ot.x = 10; //ot分配在堆上,因为ow2在堆上
    
                
            }

    或者,直接看IL代码

    或者,看汇编代码

    posted on 2018-09-11 22:46 时空观察者9号 阅读(...) 评论(...) 编辑 收藏

    展开全文
  • 内存分配方式有三种:  (1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。...(3) 从堆上分配,亦称动态内存分配。程序运行
  • C#堆内存分配和栈内存分配

    千次阅读 2015-08-24 14:46:55
    C#堆内存分配和栈内存分配  五大内存分区  C#中,内存分成5个区,他们分别是、栈、自由存储区、全局/静态存储区和常量存储区。 栈,就是那些由编译器需要的时候分配不需要的时候自动清楚的...
  • C++堆内存分配

    千次阅读 2017-03-30 08:42:46
    C++堆内存分配C堆内存分配 抽象与分层 如何扩展有效堆内存 brk和sbrk系统调用 如何维护有效堆内存 operator new抽象与分层​ c和c++的内存服务模型与计算机网络里面的协议分层模型有点类似。计算机网络协议大体分为...
  • 深入理解Java之JVM堆内存分配

    万次阅读 2017-04-20 22:16:56
    Java是被所有线程共享的一块内存区域,所有对象和数组都在堆上进行内存分配。为了进行高效的垃圾回收,虚拟机把堆内存划分成新生代、老年代和永久代(1.8中无永久代,使用metaspace实现)三块区域。 Java把内存...
  • //栈中分配内存 A * a = new A(); //动态内存分配,在堆上 要是这样,如果我尽量不用指针的话(这样程序风格似乎更加清晰一些),那是不是对象都分配到栈了?这有什么弊端吗?栈的空间是不是很有限?是不是...
  • 堆分配内存空间是否连续

    万次阅读 多人点赞 2017-01-13 16:52:36
    堆分配的空间逻辑地址是连续的,但物理地址是不连续的(因为采用了页式内存管理,windows下有段机制、分页机制),如果逻辑地址空间已经没有一段连续且足够大的空间,则分配内存失败。
  • Spark1.5堆内存分配

    千次阅读 2016-11-08 15:22:33
    Spark1.5堆内存分配 这是spark1.5及以前堆内存分配图 ...出于安全考虑和避免内存溢出,Spark只允许我们使用堆内存的90%,这spark的spark.storage.safetyFraction 参数中配置着。也许你听说的spar
  • Java中堆内存与栈内存分配浅析

    千次阅读 2013-09-24 11:01:15
    函数中定义的一些基本类型的变量和对象的引用变量都是函数的栈内存中分配,当一段代码块定义一个变量时,Java就栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存...
  • Java中的对象一定在堆上分配吗?

    千次阅读 多人点赞 2019-04-20 17:40:25
    首先,为解释这个问题,需要的基本知识如下(如果对以下概念不太熟悉, 可以先了解下): ...Java中,典型的对象不在堆上分配的情况有两种:TLAB(Thread Local Allocation Buffer)和栈上分配(严格...
  • JVM内存分配策略 关于JVM的内存结构及内存分配方式,不是本文的重点,这里只做简单回顾。以下是我们知道的一些常识: 1、根据Java虚拟机规范,Java虚拟机所管理的内存包括方法区、虚拟机栈、本地方法栈、、程序...
  • 静态存储分配是指编译时就能确定每个数据目标运行时刻的存储空间需求,因而编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者...
  • jvm内存管理-堆内存分配

    千次阅读 2017-05-12 20:06:28
    本篇介绍了Java虚拟机自动内存分配的主要规则
  • 一个socket所有的可用内存为一个。每个由大小不同的块组成,每个块是一个连续的存储器片。申请内存时可以指定(socket),或者任意,当指定为任意时,会优先使用本地(本地socket)。 结合前面说的...
  • 空间内存分配时,首地址处记录分配内存的大小会不会占用内存空间。 大小记录首地址处,如果占用了首地址的内存空间,首地址又怎么能用来存储数据了呢。 比如 char *a = new char; 分配的一个字节空间需要...
  • Java作为一种面向对象的,跨平台语言,其对象、...JVM的内存结构中,比较常见的两个区域就是堆内存和栈内存(如无特指,本文提到的栈均指的是虚拟机栈),关于和栈的区别,很多开发者也是如数家珍,有很多书籍,...
  • 堆内存空间和内存分配策略

    千次阅读 2019-09-14 22:31:20
    堆内存空间和内存分配策略 Java 主要分为2个区域-年轻代与老年代,其中年轻代又分 Eden 区和 Survivor 区,而Survivor 区又分 From 和 To 2个区。如下图: Eden区 IBM 公司的专业研究表明,新生代中有98%的对象是...
  • 上分配内存函数Alloca

    千次阅读 2019-03-21 18:49:28
    对于堆上内存分配函数,大家都比较熟悉。...并且,STL源码及其他内存分配器中,都是用Alloc表示堆上内存分配,于是就习惯性的认为alloca函数也是堆上分配内存的函数。 今天,问一个同事有没有栈分配...
  • #include&lt;stdio.h&gt; /** C中动态分配内存的基本... malloc :从堆上分配内存  realloc : 之前分配的内存块的基础,将内存重新分配为更大或者更小的部分  calloc: 从堆上分配内存并清零  fre...
  • JVM内存分配策略

    千次阅读 2019-01-23 18:00:34
    对象内存的分配主要是在堆上分配内存,对象主要分配新生代的Eden区,如果启动了本地线程分配缓冲,将按线程有限分配TLAB。少数情况可以直接分配老年代中。 TLAB技术:JVM内存新生代Eden Space中开辟了...
  • 引用类型的全局变量内存分配在堆上,值类型的全局变量分配在 局部变量内存分配可能也可能在堆上 我们都知道变量占有内存,内存在底层分配上和栈。 值类型变量的内存通常是栈中分配 引用类型变量的...
  • c++中、栈内存分配

    千次阅读 2019-06-05 12:03:41
    首先来看看主要有几种程序内存分配: 一个由C/C++编译程序占用内存分为以下几个部分 1、栈区(stack)— 由编译器自动分配释放 ,存放函数参数值,局部变量值等。其操作方式类似于数据结构中栈。 2、区(heap) ...
  • new分配内存一定在堆上么?

    千次阅读 2015-04-29 17:51:29
    运行环境 Ubuntu 14:05 #include #include #include int main (void) { int *A = new int(100); printf("%p\n",A); printf("请查看/proc/%u/maps,按回车结束....",getpid()); getchar (); ...
  • 以及理解堆是什么,栈是什么等问题,可以先看博文 对堆栈中分析的比较好的文章进行的总结:http://blog.csdn.net/jin13277480598/article/details/54406980很多人都有这样的疑问:堆分配内存空间到底是连续的还是...
  • java对象在堆内存中的分配原则

    千次阅读 2015-07-03 08:52:43
    1、对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC。 2、大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免Eden区和两个 Survivor区之间发生...
  • [收藏]C++ Tips(7)--堆内存分配

    千次阅读 2005-09-14 23:34:00
    --coofucoo*/ 在堆上分配内存可能许多人对内存分配的“栈 stack”和“ heap”还不是很明白。包括一些科班出身的人也不明白这两个概念。简单的来讲,stack分配的内存系统自动释放, heap

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 414,699
精华内容 165,879
关键字:

在堆上分配内存