精华内容
下载资源
问答
  • 上回说到如何鉴别一个垃圾。...可能出来的时候大家都发现了,这个算法有一个很明显的问题,那就是大量的不连续的内存碎片,这样的内存碎片遇到大对象分配的时候很可能遇到内存不足的 情况,当然

    上回说到如何鉴别一个垃圾。

    这回咱们讲讲怎么收集垃圾收集垃圾有几种算法如下:

    1、标记-清除算法

    这个算法最为基础,我们先讲算法再说优缺点。

    实现过程:

    标记出所有需要回收的对象,当标记完成后统一回收。图解如下:

    优缺点:

    可能画出来图的时候大家都发现了,这个算法有一个很明显的问题,那就是大量的不连续的内存碎片,这样的内存碎片遇到大对象分配的时候很可能遇到内存不足的 情况,当然出了这个情况以外还有一种问题就效率太低(可以对比之后的算法)。

    2、复制算法

    实现:把内存划分为大小相等的两块,当GC的时候,就把其中一块复制到另一块上,然后直接清理掉原本的那一半内存。

    优缺点

    这种算法要比标记清除算法效率高并且没有内存碎片,但是这样会浪费一半内存而且如果存货较多对象,复制效率也很低。

    修正与应用:

    现在商业虚拟机大多都采用这种方式回收新生代,但是不回划一半内存那么大。它们把内存氛围Eden空间(80%)和两块Survivor空间(10%+10%),每次GC时, 从Eden和一个survivor区里复制活着的对象到另一个survivor里,因为新生代 GC频繁且效率高,所以一半清除后的对象一个survivor基本可以存下,但是如果空间不够 用,就会引起老年代的分配担保(在本章稍后讲解)。

    3、标记-整理算法

    实现:

    让所有存活的对象向前端移动,最后清除后面的内存

    优缺点

    效率高于标记-清除算法,而且可以保证所有的内存都可以使用

    应用:

    这种算法可以有效的用在老年代中,因为老年代gc不频繁而且每次效率不高。

    我们了解到了怎么清理内存之后,这在之前还有一个问题,分配对象的规则是什么呢

    1、分配对象

    我们用流程图来讲解:

    1我们可以看出来一个对象在分配的时候先要看看Eden区是否可以装得下(或者设置了PretenureSizeThreshold参数,根据参数来判断)

    2、如果装不下就直接分配到老年区

    3、如果分配的下又会查询Eden空间是否是充足

    4、如果Eden剩余可用内存充足就把对象放在了Eden区域

    5、如果Eden不充足的话就会引起MinorGC(新生代GC,新生代GC效率较高大概是FullGC(老年代GC)的十倍),如果发现Eden中的对象不足以放在survivor中就 会直接放在老年代里,然后分配对象到新生代。

    2、长期存活对象转入老年代

    图解:

    特殊情况

    虚拟机中并没有严格的遵守必须年龄>=MaxTenuringThreshold才能转到老年代,如果Survivor空间中相同年龄所有对象大小的综合大雨Survivor空间的一半,年 龄>=它们的也可直接转到老年代。

    3、空间分配担保

    实现:

    在MinorGC之前每次都会查看老年代的最大连续可用空间是不是大于新生代所有总对象总空间,如果大于的话说明新生代可以转入老年代,如果空间不足保证所有的 survivor区域里的话说明不安全,因为很有可能MinorGC后晋升老年代,老年代根本存不下,所以这个时候要查看是否设置了允许冒这个险,(参数 HandlerPromotionFailure)除了检测参数外还要查看是否大于以往晋升对象的平均值,都允许的话就开始冒险。如果不允许就先FullGC。

    冒险:尝试把MinorGC后剩余的对象放入老年代,如果成功最好,省了FullGC,如果失败只能跑一次FullGC了,时间花销最大。

    图解如下:

    展开全文
  • 来自公众号:匠心零度说明在学习Netty的时候,ByteBuf随处可见,但是如何高效分配ByteBuf还是很复杂的,Netty的池化内存分配这块还是比较难的,很多人学习过,看过但是还是云里雾里的,本篇文章就是主要来讲解:...

    来自公众号:匠心零度

    说明

    在学习Netty的时候,ByteBuf随处可见,但是如何高效分配ByteBuf还是很复杂的,Netty的池化内存分配这块还是比较难的,很多人学习过,看过但是还是云里雾里的,本篇文章就是主要来讲解:Netty分配池化的堆外内存的细节,期待可以让你明白!!!

    由于为了更好的表达,文章中的图我最少画了6小时,画的不熟悉,并且也强调一些细节上。

    由于该源码中涉及到大量的二进制操作,建议看看我之前写的2篇二进制文章:java二进制相关基础,二进制实战技巧。

    ByteBuf重要性

    ByteBuf在Netty中一直存在,读写必备!ByteBuf是Netty的数据容器,高效分配ByteBuf至关重要!

    4e8f7269f841fdf7d8af9370f788fec6.pngNetty从socket读取数据。

    04c7010585d3efa77e43b6bed8c68bea.pngNetty准备把数据写到socket中去。

    ce37fb348d9e953150c714b5bc688b28.png通过这里我们就可以看到,再把数据写socket的之前会判断是否是堆外内存,如果不是会构造一个directbuffer对象的,细节代码如下:

    if (msg instanceof ByteBuf) {
                ByteBuf buf = (ByteBuf) msg;
                if (buf.isDirect()) {
                    return msg;
                }

                return newDirectBuffer(buf);
            }

    0e85bba6ae33bc43fcd9224c4c481711.png所以本篇文章就是主要来讲解:Netty分配池化的堆外内存的细节,其实分配堆内存的细节很多也是类似的。

    备注: 为什么不是堆外内存还要转堆外内存,为什么加这个判断,我之前也不理解,忽然有天和涤生大佬讨论,讨论讨论就清晰了,后续有空写篇。

    总览

    7a1cae51d2ebf03711be05c954881a5f.png

    本次主要讨论的是关于池化内存的分配,PooledByteBufAllocator就是netty分配池化内存的操作入口。

    其提供对外常用操作api:

    5608e01fda1deb10b070f798ee6a2727.png

    Netty在发送数据的时候会判断是否是堆外内存,如果不是会进行封装的:

    64d60e2288595a9b462e751008e01140.png

    所有这里我们以分配池化的堆外内存为例,进行本文说明。池化的堆内存分配其实流程都差不多的。

    下面我们来看看分配示例demo:

    public static void main(String[] args{
        ByteBufAllocator alloc = PooledByteBufAllocator.DEFAULT;

        //tiny规格内存分配 会变成大于等于16的整数倍的数:这里254 会规格化为256
        ByteBuf byteBuf = alloc.directBuffer(254);

        //读写bytebuf
        byteBuf.writeInt(126);
        System.out.println(byteBuf.readInt());

        //很重要,内存释放
        byteBuf.release();
    }

    后续我们都会根据这段简单的demo进行分析。

    操作入口类

    PooledByteBufAllocator的初始化:

    c0c9cbc9691ff14d65229a3cec660dfe.png

    进去之后可以看到核心类的一初始化操作:

    fd4d3a990074e0b3ea64d5d9becc2fbd.png

    35267098839cfe51c6f948cd4ce2d444.png

    a64b3807386d91295ec828c87a5783cf.png

    分配理论是jemalloc,可以理解为java版本的jemalloc实现。

    PoolThreadCache

    d224f109fafeddf541a0075609a55b2f.png

    通过上图可以清晰的了解到PoolThreadCache的主要数据结构。

    开始的时候,这些Cache里面都是没有值的,只有在调用free释放的时候(在后续释放内存中会讲解),才会把之前分配的内存大小放到该cache的queue里面,其实每次分配的时候都是先看看是否缓存里面有,如果有直接返回,没有则进行正常的分配流程(内存分配会讲解)。

    我们来看看PoolArena directArena内容:

    b25b5b899298f08bd1940d036c1c3cfa.png

    下面我们来看看PoolArena结构。

    PoolArena

    e39fe912471d214c11b1573ad8400201.png

    通过下图可以清晰的了解到PoolArena的主要数据结构。

    51c3bbcebe7d9f45111c6fb97a2f5e16.png

    在PoolArena里面涉及到PoolChunkList和PoolSubpage对应的结构有PoolChunk和PoolSubpage,我们来详细的看看这2块内容。

    PoolChunk

    第一次的时候,PoolChunkList、PoolSubpage都是默认值,需要新增一个Chunk,默认一个Chunk是16M。内部会结构是完全二叉树一共有4096个节点,有2048个叶子节点(每个叶子节点大小为一个page,就是8k),非叶子节点的内存大小等于左子树内存大小加上右子树内存大小。

    完全二叉树结构如下:

    6dcca4a38cebb1e4c1b5255d0c6f5031.png

    这颗完全二叉树在java中是使用数组来进行表示的。

    唯一需要注意的是,下标是从1开始而不是0.

    a4588cb43c43db3d48d3fc3e2204fe78.png

    depthMap的值初始化后不再改变,memoryMap的值则随着节点分配而改变。

    3a06ea82f1d01ef89ad9d037da385b75.png

    这个值太多就不都截图了,就是把上面那颗完全二叉树用数组表示了而已,只是值存的不是节点的下标而是存的树的深度而已。

    depthMap数组值为0表示可以分配16M空间,如果为1 表示可以分配8M,,如果为2表示嗯可以分配4M,如果为3表示可以分配2M ……………………如果为11表示可以分配8k空间。

    如果该节点已经分配完成,就设置为12即可。

    怎么确定需要分配的大小在深度是多少?

    如果需要分配的内存规格化之后,是小于8k,那么在8k上面分配即可(即深度为11)。

    如果为8k或者大于8k那么通过下面代码就可以定位到深度了:

    int d = maxOrder - (log2(normCapacity) - pageShifts);

    知道深度之后,怎么进行定位到那个节点呢???

    283bd542ea13b92a11cb52d673f676d6.png

    找到该节点之后,先把该节点显示占用,在更新起父节点父节点的父………………如下:

    eadb00b2d05340b6e1bc9cf99cf449a7.png

    SubpagePool

    152d4bee78b2cb51bccf7b86fbf482f7.png

    上面的图就是关于SubpagePool的内存结构了。我们在分配page的时候,根据memoryMap对于的值就知道是否被分配了,那么如果是subpagePool呢?

    subpagePool分为2类:tinySubpagePools和smallSubpagePools,大小对于也对于上面的图里面了,每类都是固定大小的,如果分配256b的大小,那么一个page就是8k,8*1024/256 = 32块。那么怎么怎么表示每个还被分配了呢?

    private final long[] bitmap;

    由于一个long占用的字节数为64,我们这里仅仅是需要表示32个,所以使用一个long即可了,二进制每位 1表示已经使用了,0表示还未使用。

    daf5679b3c3039f41674f1cc10a8f828.png

    由于subpage不仅仅需要定位到完全二叉树在那个节点,还需要知道在long的第几个 并且是第几位,所以要复杂一些:

    1fa35332eaade260b6eb02699c4ae55d.png

    通过一个long的前32位来表示subpage的第几个long的第几位上面,通过后32来表示在完全二叉树的那个节点上面,完美。

    分配核心

    分配入口:ByteBuf byteBuf = alloc.directBuffer(256);

    进行跟进代码:

    fc2d5883a1de18e4b277da069a44737a.png

    我们来看:PooledByteBuf buf = newByteBuf(maxCapacity);

    构建PooledByteBuf对象。最后返回PooledByteBuf对象。

    我们来看下类继承结构:

    e23c4c4dad6d660fe573ed379b3719ca.png

    所有ByteBuf byteBuf = alloc.directBuffer(256);这句话是没有什么问题的,不会报错。

    我们来看看newByteBuf(maxCapacity)的细节实现:

    8d4874b69e53642c18641652388601cd.png

    这里借助了Netty增加实现的Recycler对象池技术。Recycler设计也非常精巧,后续可以专门写篇Recycler文章,今天不是重点,我们只要知道由于分配PolledByteBuf对象的代价有点大,如果需要频繁使用到PolledByteBuf对象,并且对性能有所要求,那么池化技术是一个不错的选择(比如我们以前使用的线程池、数据库连接池等都是类似道理),池化技术在一定程度上面减少了频繁创建对象带来的性能开销。其实这个类似的思想非常常见(比如我们查询数据库成本高,缓存到redis,思路也是一样的),在本篇后续中还可以体会到(PoolThreadCache)。

    通过PooledByteBuf buf = newByteBuf(maxCapacity);仅仅是获取到了一个初始对象而已。

    分配的核心在:allocate(cache, buf, reqCapacity);

    a2c80b966cd9e668565778b3a8a2b394.png

    • 先尝试在1步骤 进行分配,根据不同的类型定位到不同的Caches,如果有进行分配直接返回。

    • 如果1步骤 分配不了,进行2步骤上面分配。

    2步骤分配细节:看看需要分配的是什么类型 page还是subpage,如果是subpage在根据看看是tinySubpagePools还是smallSubpagePools,找到对应的槽位,看看链表里是否有可用的PoolSubpage,如果有就进行分配修改标记退出,如果没有就现需要在先分配一个page了,根据chunklist的这些看看是否有合适的,如果有合适的,那么在这些已经有的chunk上面进行分配一个page (分配page也是这个情况了)

    之后在根据分配到的page,进行该请求大小的分配 (由于一个page可以存储很多同大小的数量)需要用long的位标记,表示该位置分配了,并且修改完全二叉树的父等值,分配结束。如果没有chunk那么需要新分配一块chunk之后重复上面步骤即可。

    释放核心

    释放入口 :byteBuf.release();

    进行跟进代码:

    a7e967963e543f778e33226f208b7fb1.png

    796018b9a2f8a6ca0091c1fa5e690039.png

    cabd2905d2cf7acea03b4e6f78269dbe.png

    通过这段代码我们就这段放入到相应的queue了:

    d224f109fafeddf541a0075609a55b2f.png

    缓存到了对应的Cache的queue里面了。


    ●编号1027,输入编号直达本文

    ●输入m获取文章目录

    推荐↓↓↓ 

    6cc92b6cff21f0f24531071a3c3b5a3f.png

    Web开发

    展开全文
  • libc 中提供非常好用的 malloc free 功能,如果自己实现一个,应该...如下所示,堆从高向低分配,链表从低向高分配是 ps 的。 1 /* 管理堆的链表 */ 2 typedef struct A_BLOCK_LINK 3 { 4 ch...

    libc 中提供非常好用的  malloc free 功能,如果自己实现一个,应该怎么做。

    要实现 malloc free 需要有 可以分配内存使用的堆,和记录内存使用情况的链表。

    如下图所示,堆从高向低分配,链表从低向高分配,图是 ps 画的。

     1 /* 管理堆的链表 */
     2 typedef struct A_BLOCK_LINK
     3 {
     4     char *pV;                                /* 内存实际物理地址 */
     5     int  index;                             /* 内存编号 */  
     6     char empty;                             /* 1 空 0 非空  */
     7     int  heapLen;                           /* 分配长度 */   
     8     struct A_BLOCK_LINK *pxNextFreeBlock;    /* 上个节点 */
     9     struct A_BLOCK_LINK *pxPrevFreeBlock;    /* 下个节点 */
    10 } BlockLink_t;

     

    这里的对应关系是,链表 1 对应 最后一个堆,链表 2对应 最后2个堆。

    如何初始化?

    1,先计算出来,共能分配多少块内存

    2,分配管理链表

    如何找到合适的可以分配的内存?

    这里从链表1 开始向后查找,当未使用的内存长度满足要求时,将最后找到的链表的,堆地址返回给 申请者。

    如何free 内存?

    因为 free 只传过来一个 ,最初申请的内存地址,没有传入长度,这里也需要在 链表结构体中进行记录

    详细的代码,可以去置顶的 github 项目。 

    经过测试在, ubuntu16.4 gcc 5.4 中运行正常,free 后的内存,可以被重新 malloc 。

    目前还没有实现,内存碎片整理功能。仅有一个空实现。 后期在更新。

    实现代码:

      1 #include <string.h>
      2 #include <stdio.h>
      3 #include "sram.h"
      4 
      5 //以下定义大小是为了直观的看到程序运行分配结果,取10进制数
      6 //整个SRAM大小
      7 #define SRAM_SIZE (1000)
      8 
      9 //堆分块大小
     10 #define HeapBlockSIZE (100)
     11 
     12 //以下代码在 GCC 中演示使用编译器分一个大的SRAM 在实际硬件中,直接写内存开始地址就可以
     13 //#define SRAM_BASE_ADDR 0
     14 static char SRAM_BASE_ADDR[SRAM_SIZE] = {0};
     15 
     16 /* 管理堆的链表 */
     17 typedef struct A_BLOCK_LINK
     18 {
     19     char *pV;                                /* 内存实际物理地址 */
     20     int  index;                             /* 内存编号 */  
     21     char empty;                             /* 1 空 0 非空  */
     22     int  heapLen;                           /* 分配长度 */   
     23     struct A_BLOCK_LINK *pxNextFreeBlock;    /* 上个节点 */
     24     struct A_BLOCK_LINK *pxPrevFreeBlock;    /* 下个节点 */
     25 } BlockLink_t;
     26 
     27 //头节点
     28 static char *HeapHead = NULL;
     29 //块数量
     30 static int BlockTotal = 0;
     31 
     32 void TraceHeap(void)
     33 {
     34     BlockLink_t *pTempBlockLink = (BlockLink_t *)HeapHead;
     35     printf("\r\n##########TraceHeap#############\r\n");
     36     while(pTempBlockLink)
     37     {
     38         printf("index: %d empty:%d addr:%d \r\n", pTempBlockLink->index, pTempBlockLink->empty, pTempBlockLink->pV - SRAM_BASE_ADDR);
     39         pTempBlockLink = pTempBlockLink->pxNextFreeBlock;
     40     }
     41     printf("\r\n##########TraceHeap#############\r\n");
     42 }
     43 
     44 //堆 Heap 分配初始化
     45 void InitHeap(void)
     46 {
     47     BlockLink_t *pBlockLink, *pTempBlockLink = NULL;
     48     int i = 0;
     49     char *pHeapStart = (char *)SRAM_BASE_ADDR;
     50     char *pHeapEnd   = (char *)(SRAM_BASE_ADDR + SRAM_SIZE);
     51     pHeapEnd -= HeapBlockSIZE;
     52     BlockTotal = SRAM_SIZE / (HeapBlockSIZE + sizeof(BlockLink_t));
     53     while(i < BlockTotal)
     54     {
     55         pBlockLink          = (BlockLink_t *)pHeapStart;
     56         pBlockLink->pxPrevFreeBlock = pTempBlockLink;
     57         pBlockLink->pV      = pHeapEnd;
     58         pBlockLink->index   = i;
     59         pBlockLink->empty   = 1;
     60         pBlockLink->heapLen = 0;
     61         //指针重定位
     62         pHeapEnd   -= HeapBlockSIZE;
     63         pHeapStart += sizeof(BlockLink_t);
     64         //双向链表的上一个
     65         pTempBlockLink = pBlockLink;
     66         pBlockLink->pxNextFreeBlock = (BlockLink_t *)pHeapStart;    
     67         i++;
     68     }
     69     pBlockLink->pxNextFreeBlock = NULL;
     70     HeapHead = (char *)SRAM_BASE_ADDR;
     71 }
     72 
     73 static BlockLink_t *FindHeap(char *addr)
     74 {
     75     BlockLink_t *pTempBlockLink = (BlockLink_t *)HeapHead;
     76     //从低向高查找可用的内存
     77     while(pTempBlockLink)
     78     {
     79         if(pTempBlockLink->pV == addr)
     80         {
     81             return pTempBlockLink;
     82         }
     83         //切换下一节点
     84         pTempBlockLink = pTempBlockLink->pxNextFreeBlock;
     85     }
     86     return NULL;
     87 }
     88 
     89 //查找可用的连续内存
     90 static void *SramFindHeap(int size)
     91 {
     92     char *mem;
     93     int seriesSize = 0;           //已查找到连续用的内存
     94     BlockLink_t *pTempBlockLink;  //头节点
     95     pTempBlockLink = (BlockLink_t *)HeapHead;
     96     //从低向高查找可用的内存
     97     while(pTempBlockLink)
     98     {
     99         //如果是未使用的内存
    100         if(pTempBlockLink->empty)
    101         {
    102             seriesSize += HeapBlockSIZE;
    103         }
    104         else
    105         {
    106             seriesSize = 0;
    107         }
    108         //如果查找到连续未使用的内存
    109         if(seriesSize >= size)
    110         {    
    111             //返回内存堆开始地址 
    112             pTempBlockLink->heapLen = seriesSize; //设置分配堆长度
    113             mem = pTempBlockLink->pV;
    114             
    115             //将内存标记为 已使用
    116             while(seriesSize && pTempBlockLink)
    117             {
    118                 seriesSize -= HeapBlockSIZE;
    119                 pTempBlockLink->empty = 0;
    120                 pTempBlockLink = pTempBlockLink->pxPrevFreeBlock;
    121             }
    122             return mem;
    123         }
    124         //切换下一节点
    125         pTempBlockLink = pTempBlockLink->pxNextFreeBlock;
    126     }
    127     return NULL;
    128 }
    129 
    130 //内存碎片整理
    131 static void SramMerge(void)
    132 {
    133     
    134 }
    135 
    136 void *SramMalloc(size_t xWantedSize)
    137 {
    138     char *mem;
    139     
    140     if(! HeapHead)
    141     {
    142         InitHeap();
    143     }
    144     //地址对齐
    145     
    146     mem = SramFindHeap(xWantedSize);
    147     if(mem)
    148     {
    149         return mem;
    150     }
    151     //如果没有查找到 整理内存碎片
    152     SramMerge();
    153     
    154     //仍然分配不成功 返回错误
    155     mem = SramFindHeap(xWantedSize);
    156     if(mem)
    157     {
    158         return mem;
    159     }
    160     return NULL;
    161 }
    162 
    163 void SramFree( void *pv )
    164 {
    165     int heapLen = 0;
    166     //释放内存 从堆的高位开始向低位查找
    167     BlockLink_t *pTempHeap = FindHeap(pv);
    168     heapLen = pTempHeap->heapLen;
    169     while(heapLen && pTempHeap)
    170     {
    171         //设为空闲状态
    172         pTempHeap->empty   = 1;
    173         pTempHeap->heapLen = 0;
    174         //查找上个节点
    175         pTempHeap = pTempHeap->pxPrevFreeBlock;
    176         heapLen -= HeapBlockSIZE;
    177     }
    178 }

     

    转载于:https://www.cnblogs.com/ningci/p/7455499.html

    展开全文
  • 我们学到了Java虚拟机中内存分配,但是Java是一门面向对象的编程语言,在Java程序运行过程中无时无刻都有对象被创建出来,在语言层面上,我们只知道只要一new,对象就被创建了出来,但是在Java虚拟机中,对象...

    在上一章节,我们学到了Java虚拟机中内存的分配,但是Java是一门面向对象的编程语言,在Java程序运行过程中无时无刻都有对象被创建出来,在语言层面上,我们只知道只要一new,对象就被创建了出来,但是在Java虚拟机中,对象(这里的对象 仅仅是指普通的Java对象,不包括数组和Class对象等)创建又是怎么样的一个过程呢?

    下面的是我画的创建对象虚拟机内的一系列过程概图:

    123

    第一步:虚拟机遇到一条new指令时,首先会去检查这个指令的参数是否能在常量池定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载,解析和初始化过,如若没有,则必须要先执行相应的类加载过程。

    第二步:在类加载通过之后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后便已经完全确定,只需为在Java堆中划分一块确定大小的内存给新生对象。

    这里我们就要分为两种情况了,:从Java堆是完整的内存,所有用过的内存放一边,空闲的内存放一边,只需在中间放一个指针作为分界点的指示器,那么分配内存就仅仅是把那个指针挪动一段与对象大小相等的距离,这种分配方法也称为“指针碰撞”。

    :从Java堆是 一块并不完整的内存,空闲的内存和已经使用过的内存相互交错。虚拟机就必须维护一个表,记录哪些内存块是可用的,在分配的时候找一块足够大的空间划分给新生对象,并更新列表上的内容,这种分配方法称为“空闲列表”。

    注:在 划分空间 的时候,不仅要考虑的是如何划分空间,还要考虑到安全问题,比如:在给对象A分配内存时,指针还没来得及修改,对象B又同时使用了原来的指针的分配内存的情况,就会产生内存冲突问题。如何解决呢?有两种方案:

    方案A:对分配内存空间的动作进行同步处理

    方案B:把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB),哪个线程要分配内存,就在哪个线程的TLAB上分配。

    第三步:虚拟机将分配搭配的内存空间都初始化为零值(不包括对象头),如果使用了TLAB。这一工作过程也可以提前至TLAB分配时进行。(这一步操作了对象的实例字段在Java代码中可以不赋初值就直接使用,程序能访问到这些字段的数据类型所对应的零值)

    第四步:虚拟机要对对象进行必要的设置,比如这个对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等信息,这些信息存放在对象的对象头之中。(关于对象头的具体内容,之后再做介绍)

    第五步:在上面四步的工作完成之后,一个新的对象已经产生了但从Java程序的视角来看,对象创建才刚刚开始,但是<init>方法还没有执行,所有的字段都还为零,所以,一般来说,执行new指令之后会接着执行<init>方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全的产生出来。

    那么,在虚拟机中,对象在内存中存储的布局是怎样的布局呢?在HotSpot虚拟机中,对象在内存中存储的布局可分为3块区域:对象头,实例数据和对齐填充。

     

     

    展开全文
  • 一直想不通为什么看人家画内存图都会把虚拟成员放到最后,如何通过虚表确定虚拟变量唯一的呢? 大概是在初始化之前编译器已经根据代码分配好了内存,如上,B和C都虚继承于A,也许D继承于B,C ,这样D的内存分布...
  • //分配内存空间 storage = cvCreateMemStorage(0); //人数统计 if(cascade) { //检测窗口每次放大1.1倍 double scale = 1.1; int i; //使用分类器进行人脸检测 ...
  • readonly和const常常有人搞不清楚它们到底怎么用,有什么区别,实际上清楚变量的在内存中是怎么分配内存的就非常清楚了,上一文我就很好的说明了值类型和引用类型内存分配机制,如果看过了那么下面的那就...
  • 写了一个直方对话框,要在上面直方,画图的时候要用到的数据在主对话框里,怎么用其中的数据呢? 1,全局变量一:1,在任何一个CPP文件的函数体外声明(不能加关键字static,否则链接性为内部,只能在该文件...
  • C#——类和继承

    热门讨论 2015-12-13 21:18:17
    前言之前的UMl中接触到了类,但对类是什么,怎么创建,怎么调用没有清晰的认识。现在学习C#,这门面向对象语言,可算领略到了类的魅力。类很大程度上减少了代码的冗余,增加了代码的灵活性。类的基础知识(1)类...
  • Effective C++ 条款13

    2017-06-16 14:13:00
    资源的种类非常多,动态分配内存、文件描写叙述器、相互排斥锁、图像界面中刷、数据库连接、网络socket等。资源通常是有限的。当你不用时,必须释放。不然就会造成资源浪费。更严重的情况下,非法占有全部资源...
  • (内存分配篇) | 内存有哪些分配方式? < csdn | oschina > (内存管理篇) | 虚拟内存全景是怎样的? < csdn | oschina > (内存主奴篇) | 紫禁城的主子和奴才如何相处? < csdn | oschina > ...
  • MAPGIS地质制图工具

    2013-05-06 16:15:30
    10、剩下的工作就是对出的剖面进行添加数据和修饰面了。 大致的过程就是这样的。希望能对大家有所帮助。 (以后的版本部分菜单名称会有所改变,但基本过程不变) 编辑本段辅助工具Ⅰ 辅助工具Ⅰ包含以下功能...
  • 软件工程教程

    热门讨论 2012-07-06 23:10:29
    用例只描述参与者和系统在交互过程中做些什么,并不描述怎么做。 用例 关联关系 用例 泛化关系 用例 泛化关系 用例 用例 用例 用例用于什么情况? 不知道什么情况不用用例 如果没有用到用例,...
  • 1 理解计算机是怎么运行程序的 2 运行一个已解释的程序 3 运行一个已编译的程序 4 C++在哪里 5 理解Visual c++中的程序文件 6 创建源代码文件 7 理解并创建头文件 第二章 结构和语法 8 理解计算机语言 9 理解计算机...
  • 1 理解计算机是怎么运行程序的 2 运行一个已解释的程序 3 运行一个已编译的程序 4 C++在哪里 5 理解Visual c++中的程序文件 6 创建源代码文件 7 理解并创建头文件 第二章 结构和语法 8 理解计算机语言 9 理解计算机...
  • 1 理解计算机是怎么运行程序的 2 运行一个已解释的程序 3 运行一个已编译的程序 4 C++在哪里 5 理解Visual c++中的程序文件 6 创建源代码文件 7 理解并创建头文件 第二章 结构和语法 8 理解计算机语言 9 理解计算机...
  • 1 理解计算机是怎么运行程序的 2 运行一个已解释的程序 3 运行一个已编译的程序 4 C++在哪里 5 理解Visual c++中的程序文件 6 创建源代码文件 7 理解并创建头文件 第二章 结构和语法 8 理解计算机语言 9 理解计算机...
  • 对Demo的使用介绍在官方文档的后面才开始介绍,这里建议应用跑起来之后,先自己试试手(可看后面介绍Demo如何使用的章节),看看如何跑一个流程、整个流程是怎么流的、并随时关注数据库表里的数据的变化等,对以后的...
  • 12.1.2 内存分配 12.1.3 将文本传送到剪贴板 12.1.4 从剪贴板上获取文本 12.1.5 打开和关闭剪贴板 12.1.6 剪贴板和unicode 12.2 复杂的剪贴板用法 12.2.l 利用多个数据项 12.2.2 延迟生成 ...
  • 12.1.2 内存分配 12.1.3 将文本传送到剪贴板 12.1.4 从剪贴板上获取文本 12.1.5 打开和关闭剪贴板 12.1.6 剪贴板和unicode 12.2 复杂的剪贴板用法 12.2.l 利用多个数据项 12.2.2 延迟生成 ...
  • 疯狂JAVA讲义

    2014-10-17 13:35:01
    学生提问:能不能只分配内存空间,不赋初始值呢?89 4.5.4 使用数组 90 学生提问:为什么要我记住这些异常信息? 91 4.5.5 JDK1.5提供了foreach循环 91 4.6 深入数组 93 4.6.1 内存中的数组 93 学生提问:为...
  • 10、分配资源是否已正确释放 第28页 【案例1.10.1】 第28页 【案例1.10.2】 第29页 【案例1.10.3】 第30页 【案例1.10.4】 第32页 【案例1.10.5】 第33页 【案例1.10.6】 第35页 【案例1.10.7】 第38页 11、防止资源...
  • 1.3.5 怎么能让应用运行得更快 42 1.3.6 DBA与开发人员的关系 44 1.4 小结 45 第2章 体系结构概述 46 2.1 定义数据库和实例 47 2.2 SGA和后台进程 52 2.3 连接Oracle 54 2.3.1 专用服务器 54 2.3.2 共享...
  • 1.3.5 “怎么能让应用运行得更快?” 41 1.3.6 DBA与开发人员的关系 45 1.4 小结 46 第2章 体系结构概述 47 2.1 定义数据库和实例 48 2.2 SGA和后台进程 53 2.3 连接Oracle 56 2.3.1 专用服务器 56 2.3.2 ...
  • Dart可以在没有锁的情况下进行对象分配和垃圾回收。就像JavaScript一样,Dart避免了抢占式调度和共享内存(因而也不需要锁)。由于Flutter应用程序被编译为本地代码,因此它们不需要在领域...
  • asp.net知识库

    2015-06-18 08:45:45
    怎么在ASP.NET 2.0中使用Membership asp.net 2.0-实现数据访问(1) ASP.NET 2.0 新特性 .NET 2.0里使用强类型数据创建多层应用 在MastPage中引用脚本资源 2.0正式版中callback的一些变化+使用示例(ASP.NET 2.0)...
  • c#学习笔记.txt

    热门讨论 2008-12-15 14:01:21
    例如,如果声明一个含有 1000 个点对象的数组,则将为引用每个对象分配附加的内存。结构可以声明构造函数,但它们必须带参数。声明结构的默认(无参数)构造函数是错误的。总是提供默认构造函数以将结构成员初始化为...

空空如也

空空如也

1 2
收藏数 30
精华内容 12
关键字:

内存分配图怎么画