精华内容
下载资源
问答
  • 本期文章就告诉大家“8G内存与16G内存”的差距在哪内存的概述众所周知,一台电脑包含很多零件,如电源、主板、硬盘、显卡、内存条、CPU等,其中CPU是电脑的核心,硬盘是储存数据的,而显卡是在处理一些复杂、精细...

    很多朋友在选择笔记本电脑时,因为自己不懂电脑配置,总是纠结于电脑内存的大小,设置不知道8G内存与16G内存差别大不大,或者说有什么差别。本期文章就告诉大家“8G内存与16G内存”的差距在哪。

    8ae95a82e636363fd33b17c185322686.png

    内存的概述

    众所周知,一台电脑包含很多零件,如电源、主板、硬盘、显卡、内存条、CPU等,其中CPU是电脑的核心,硬盘是储存数据的,而显卡是在处理一些复杂、精细图像时用,主板的作用就是将这些部件串联起来。

    而内存的作用就是将“外存”与CPU串联在一起,而所谓的外存指的就是电脑硬盘,可以说内存是体现电脑性能的重要部件之一,它的大小可以影响电脑的运行速度。

    6b96bcd71f02dd59de697ffb9bc09063.png

    8G内存与16G内存的区别

    如果我们把内存看作一座桥,那么16G的桥肯定要比8G的桥宽大很多。而内存作为CPU沟通硬盘的桥梁存在,桥越快交互数据越快。而且电脑在运行各种软件时,虽然读取的是硬盘内的软件数据,但运行的载体实际上就是内存,内存越大就代表着系统可以运行的软件就越多(理论上,电脑是要看综合性能的,后面会讲到“。

    所以,如果其他配置相同的情况下,16G内存的电脑运行速度要高于8G内存的电脑运行速度,而且不是一加一等于二那么简单。

    27e60c3fe73c7df4d301f3191391046c.png

    性能区别要看整体

    从本质上讲,内存条越大对系统性能的提升就越大,但这只是理论上的。要知道电脑其他零部件也是有配置参数的,如果一台电脑的其他部件性能优秀,安装8G内存可能就无法发挥其他部件的性能。反过来也一样,如果其他部件垃圾,我们给他安装了一条16G的内存,这也是无法发挥内存条的性能的,有种大马拉小车的感觉。

    如果单纯的论性能和区别的话,在同样频率、同样代系、同样品牌的情况下,两条8G内存条的性能才能与16G内存条的性能相同。但这只是理论上的数据,因为主板内存卡槽是分通道的,而且两条内存条兼容性上也有可能存在问题,所以两条8G内存条不能与一条16G内存化等号。

    总的来说,16G内存条比8G内存的性能好2倍以上。

    展开全文
  • 系统跑着跑着,就会产生异常crash而死机了,查到原因是系统动态内存耗尽了,即出现了典型的内存泄漏,这个时候应该从着手查起呢?如果你也有这些困扰,那就过来吧!”01—系统内存调试诊断背景开发过程中,...

    “ 系统总内存怎么查询?系统剩余可用内存还有多少呢,我的应用需要malloc 100KB空间,能成功吗?查询到的系统可用内存还有200KB,为什么我连50KB都申请不到了呢?系统跑着跑着,就会产生异常crash而死机了,查到原因是系统动态内存耗尽了,即出现了典型的内存泄漏,这个时候应该从哪着手查起呢?如果你也有这些困扰,那就看过来吧!

    01

    系统内存调试诊断背景

      

    在开发过程中,我们总是会遇到诸如:

    • 系统总内存怎么查询?

    • 系统剩余可用内存还有多少呢,我的应用需要malloc 100KB空间,能成功吗?

    • 查询到的系统可用内存还有200KB,为什么我连50KB都申请不到了呢?

    • 系统跑着跑着,就会产生异常crash而死机了,查到原因是系统动态内存耗尽了,即出现了典型的内存泄漏,这个时候应该从哪着手查起呢?

    上述的内存问题总是让人头痛,因此AliOS Things给大家提供了一套强大的内存问题分析定位方法,即:

    • 内存专用的CLI命令

    • 内存查询API接口

    • 内存dump机制

    • 内存解析工具

    结合这三种方法,我们希望尽可能给开发者提供系统内存分析,帮助开发者进一步定位内存问题。

    02

    打开相关组件

    内存问题分析,依赖AliOS Things提供的debug组件和cli组件。

    打开步骤如下:

    step1:在Config.in里select debug组件和CLI组件。

        举例说明,我们当前使用的是helloworld_demo,那我们进入application/example/helloworld_demo/Config.in,增加2行代码

    select AOS_COMP_DEBUGselect AOS_COMP_CLI

    e93005626ca64768581515fa7c7ffee9.png

    Step2:使能内存调试宏RHINO_CONFIG_MM_DEBUG,方法是在/platform/boarf/haas1000/config/k_config.h里打开

    #ifndef RHINO_CONFIG_MM_DEBUG#define RHINO_CONFIG_MM_DEBUG                1#endif

    Step3重新编译上电

    aos make distcleanaos make helloworld_demo@haas100 -c configaos make

    Step4 烧录,参考 HaaS100快速开始

    Step5:上电进入系统后,执行cli 命令,系统启动后,操作方法与其他的常用shell类似。按回车有#符号打印,输入help可查看系统自带的几个cli命令,表示系统已经支持cli 命令的输入。

    3e07904be8afe8c4d7c20e7403889e9d.png

    03

    内存CLI命令

    内存状态查询

    执行dumpsys mm 查看系统内存状态,会有下面的打印输出(举例):

    dumpsys mm

    92662fade13d56b73ca7bdabf04168dc.png

    字段解释

    HEAP中的内容含义(单位为字节):

    • TotalSz:系统可供malloc的动态内存总大小;

    • FreeSz:系统当前空闲内存大小;

    • UsedSz:系统当前已经分配的内存大小,即UsedSz = TotalSz – FreeSz;

    • MinFreeSz:系统空闲内存的历史最小值,即TotalSz – MinFreeSz 便是内存历史使用量峰值;

    • MaxFreeBlkSz:系统最大空闲块Size,表示系统此时可供分配出来的内存最大值。

    上面各字段就可以回答本文开头的一些问题:

    “系统总内存怎么查询”—— TotalSz

    “系统剩余可用内存”—— FreeSz

    “查询到的系统可用内存还有200KB,为什么我连50KB都申请不到了呢?” —— 查看MaxFreeBlkSz,这个字段表示系统此时可供分配出来的内存最大值。如果系统大量存在非常小段内存的不断malloc和free的情况,系统会产生一些内存碎片,这个时候即使系统可用内存(FreeSz)还够,也只能最大分配出MaxFreeBlkSz的内存。关于AliOS Things使用的内存算法和对内存碎片的处理,请读者关注我们后续的文章。

    内存log dump

    执行dumpsys mm_info 可将系统的详细动态内存信息dump出来,如果系统有很多的内存malloc,这一步可能会dump的时间比较久,示例如下:

    dumpsys mm_info
    ------------------------------- all memory blocks --------------------------------- g_kmm_head = 34002240ALL BLOCKSaddress,  stat   size     dye     caller   pre-stat    point0x34002318  used       8  fefefefe  0x0        pre-used;0x34002330  used      32  fefefefe  0x229ad7   pre-used;0x34002360  used      32  fefefefe  0x22b5cf   pre-used;0x34002390  used      40  fefefefe  0x22d14f   pre-used;0x340023c8  used      32  fefefefe  0x208e03   pre-used;0x340023f8  used      40  fefefefe  0x22c3af   pre-used;0x34002430  used      40  fefefefe  0x22c3af   pre-used;0x34002468  used      40  fefefefe  0x22d14f   pre-used;0x340024a0  used      32  fefefefe  0x229ad7   pre-used;0x340024d0  used      32  fefefefe  0x22b5cf   pre-used;0x34002500  used      80  fefefefe  0x207b67   pre-used;0x34002560  used      32  fefefefe  0x229ad7   pre-used;0x34002590  used      32  fefefefe  0x22b5cf   pre-used;0x340025c0  used      40  fefefefe  0x22d14f   pre-used;0x340025f8  used      32  fefefefe  0x229ad7   pre-used;0x34002628  used      32  fefefefe  0x22b5cf   pre-used;0x34002658  used  131072  fefefefe  0x22d865   pre-used;0x34022668  used     200  fefefefe  0x22d86f   pre-used;0x34022740  used    2048  fefefefe  0x1c5d8979 pre-used;0x34022f50  used      40  fefefefe  0x1c5ddc59 pre-used;0x34022f88  used     800  fefefefe  0x1c5d671b pre-used;0x340232b8  used    8192  fefefefe  0x22d865   pre-used;0x340252c8  used     200  fefefefe  0x22d86f   pre-used;0x340253a0  used      32  fefefefe  0x1c5d9055 pre-used;0x340253d0  used      40  fefefefe  0x1c5d5cf9 pre-used;0x34025408  used    5120  fefefefe  0x1c5dd8fd pre-used;0x34026818  used      80  fefefefe  0x1c5d5da7 pre-used;0x34026878  used    3072  fefefefe  0x22d865   pre-used;0x34027488  used     200  fefefefe  0x22d86f   pre-used;0x34027560  used      40  fefefefe  0x1c5d7901 pre-used;0x34027598  used      40  fefefefe  0x1c5d7935 pre-used;0x340275d0  used      40  fefefefe  0x22c3af   pre-used;0x34027608  used      32  fefefefe  0x1c5dde4f pre-used;0x34027638  used      32  fefefefe  0x1c5d90e7 pre-used;0x34027668  used      40  fefefefe  0x1c5d8e03 pre-used;0x340276a0  used      40  fefefefe  0x1c5d5cf9 pre-used;0x340276d8  used      32  fefefefe  0x1c5d9161 pre-used;0x34027708  used      32  fefefefe  0x1c5d916b pre-used;0x34027738  used     432  fefefefe  0x2078a7   pre-used;0x340278f8  used    1024  fefefefe  0x207dc7   pre-used;0x34027d08  used     512  fefefefe  0x1c5d6251 pre-used;0x34027f18  free  6660872  abababab  0x0        pre-used; free[(nil)   ,(nil)   ] 0x34682230  used  sentinel  fefefefe  0x0        pre-free [0x34027f18];----------------------------- all free memory blocks ------------------------------- freelist bitmap: 0x20000address,  stat   size     dye     caller   pre-stat    point0x34027f18  free  6660872  abababab  0x0        pre-used; free[(nil)   ,(nil)   ] ------------------------- memory allocation statistic ------------------------------ ---------------------------------------------------------------------------[HEAP]| TotalSz    | FreeSz     | UsedSz     | MinFreeSz  | MaxFreeBlkSz  |      | 0x00680000 | 0x0065A308 | 0x00025CF8 | 0x0065A0F8 | 0x0065A308    |--------------------------------------------------------------------------------------------number of alloc times:-----------------[2^06] bytes:    33   |[2^07] bytes:     1   |[2^08] bytes:     3   |[2^09] bytes:     1   |[2^10] bytes:   987   |[2^11] bytes:     1   |[2^12] bytes:     2   |[2^13] bytes:     1   |[2^14] bytes:     1   |[2^15] bytes:     0   |[2^16] bytes:     0   |[2^17] bytes:     0   |[2^18] bytes:     1   |[2^19] bytes:     0   |[2^20] bytes:     0   |[2^21] bytes:     0   |[2^22] bytes:     0   |[2^23] bytes:     0   |[2^24] bytes:     0   |[2^25] bytes:     0   |[2^26] bytes:     0   |[2^27] bytes:     0   |[2^28] bytes:     0   |[2^29] bytes:     0   |

    字段解释

    重点关注的是“all memory blocks”下面的log,其中几个关键的字段含义为:

    • address:用户malloc后,系统分配出来的地址,即malloc的返回值

    • stat:内存状态,used表示malloc后内存还在使用,free表示这块内存已经释放

    • size:malloc的大小(单位:字节)

    • caller:调用malloc的地方,即这块内存的使用者是谁。

    • dye/pre-stat:AliOS Things使用的内存管理算法相关,这里不赘述。

    查看内存的API接口

    如果需要在代码中调用内存状态查询,AliOS Things也提供了如下API接口,该接口的输出与上述执行dumpsys mm的输出一致。

    //需要包含的头文件#include "aos/debug.h"//接口调用方法aos_debug_mm_overview(NULL)

    内存log解析工具的使用

    面对上面dumpsys mm_info命令输出的这么多log,我们怎么分析呢?

    配合使用coredump_parser.py(路径在/components/utility/debug_tools/)解析为:

    ========== Show MM Statistic Info  ==========-------------------------------------------------------------------------------------------------------------------------------------------------------------- Alloc Addr  |           Func           | Cnt  |  Total Size  |   Line   |       File      --------------------------------------------------------------------------------------------------------------------------------------------------------------  0x208e03   |          srand           |  1   |      32      |          |     reent.c      0x1c5d9055  | aos_register_event_filter |  1   |      32      |   131    | /workspace/hass/AliOS-Things/components/utility/yloop/src/local_event.c 0x1c5dde4f  |    vfs_inode_set_name    |  1   |      32      |    25    | /workspace/hass/AliOS-Things/core/vfs/vfs_inode.c 0x1c5d90e7  |      aos_loop_init       |  1   |      32      |    85    | /workspace/hass/AliOS-Things/components/utility/yloop/src/yloop.c 0x1c5d9161  |     aos_poll_read_fd     |  1   |      32      |   113    | /workspace/hass/AliOS-Things/components/utility/yloop/src/yloop.c 0x1c5d916b  |     aos_poll_read_fd     |  1   |      32      |   114    | /workspace/hass/AliOS-Things/components/utility/yloop/src/yloop.c 0x1c5ddc59  |     vfs_lock_create      |  1   |      40      |    17    | /workspace/hass/AliOS-Things/core/vfs/vfs_adapt.c 0x1c5d7901  |      kv_lock_create      |  1   |      40      |    33    | /workspace/hass/AliOS-Things/core/kv/kv_adapt.c 0x1c5d7935  |      kv_sem_create       |  1   |      40      |    82    | /workspace/hass/AliOS-Things/core/kv/kv_adapt.c 0x1c5d8e03  |        event_open        |  1   |      40      |    40    | /workspace/hass/AliOS-Things/components/utility/yloop/src/device.c  0x207b67   |        localtime         |  1   |      80      |          |     reent.c      0x1c5d5cf9  |      aos_mutex_new       |  2   |      80      |   147    | /workspace/hass/AliOS-Things/core/osal/aos/rhino.c 0x1c5d5da7  |      aos_queue_new       |  1   |      80      |   349    | /workspace/hass/AliOS-Things/core/osal/aos/rhino.c  0x22d14f   |  krhino_sem_dyn_create   |  3   |     120      |   102    | /workspace/hass/AliOS-Things/core/rhino/k_sem.c  0x22c3af   | krhino_mutex_dyn_create  |  3   |     120      |   125    | /workspace/hass/AliOS-Things/core/rhino/k_mutex.c  0x229ad7   |      osThreadCreate      |  4   |     128      |   250    | /home/william.wgj/work/code/shenmu_lite/shenmu_github/shenmu_aos/platform/mcu/haas1000                                                                           /drivers/out/haas1000_normalization/../../rtos/rhino/cmsis/cmsis_os.c  0x22b5cf   | krhino_event_dyn_create  |  4   |     128      |   102    | /workspace/hass/AliOS-Things/core/rhino/k_event.c  0x2078a7   |       __sfmoreglue       |  1   |     432      |          |     reent.c      0x1c5d6251  |       proc_onecmd        |  1   |     512      |   156    | /workspace/hass/AliOS-Things/core/cli/cli.c  0x22d86f   |     task_dyn_create      |  3   |     600      |   280    | /workspace/hass/AliOS-Things/core/rhino/k_task.c 0x1c5d671b  |         cli_init         |  1   |     800      |   697    | /workspace/hass/AliOS-Things/core/cli/cli.c  0x207dc7   |       __smakebuf_r       |  1   |     1024     |          |     reent.c      0x1c5d8979  |      hal_uart_init       |  1   |     2048     |   333    | /workspace/hass/AliOS-Things/platform/mcu/haas1000/hal/uart.c 0x1c5dd8fd  |     uring_fifo_init      |  1   |     5120     |    25    | /workspace/hass/AliOS-Things/components/dm/ulog/ulog_ring_fifo.c  0x22d865   |     task_dyn_create      |  3   |    142336    |   275    | /workspace/hass/AliOS-Things/core/rhino/k_task.c--------------------------------------------------------------------------------------------------------------------------------------------------------------

    使用方法

    1. 将串口输出的异常log 拷贝至coredump_parser.py的同级目录中,文件名任意,这里取名为log

    2. 将elf文件也拷贝至coredump_parser.py的同级目录,elf 路径为out/helloworld_demo@haas100/binary/helloworld_demo@haas100.elf

    3. 执行下面命令(举例)

    python coredump_parser.py log helloworld_demo@haas100.elf

    如果系统提示如“arm-none-eabi-gcc”找不到,表示使用的工具链没有在系统PATH下,根据提示,我们可以在命令后加上-p 指定工具链路径(推荐),如:

    python coredump_parser.py log.txt helloworld_demo@haas100.elf -p /workspace/hass/AliOS-Things/build/compiler/gcc-arm-none-eabi/Linux64/bin/

    这个工具会将系统内存malloc情况输出到表格中,并且按照Total Size从小到大排序,同时指出了每个内存块的申请者caller在代码中的位置,一目了然。

    如果开发者定位或者怀疑此时系统内出现了内存泄漏,那么经过上面的解

    析出的系统内存使用,我们可以直接看出哪个模块的内存只有申请没有释放了,一般是Total Size 最大的那个点!

    04

    开发者技术支持

    系统内存问题的定位比较复杂,使用上面几个方法,可以有效的帮助开发者定位排查一些内存问题,希望大家多多尝试上面的命令,灵活使用,才能发挥出AliOS Things的诊断调试的强大功能。

    如需更多技术支持,可加入钉钉开发者群

    abf017b05d366918f297a95b39a19147.png

    展开全文
  •  kmemleak只监控大小为256的内存块 我如下三个函数中加了限制,size不为256的时候直接返回,不调用create_object函数创建监控对象 <p>kmemleak_alloc ; margin-right:0cm">kmemleak_alloc_percpu ...
  • LWIP内存管理之动态内存

    千次阅读 2018-03-30 17:39:33
    无论在哪一种系统中,动态内存管理都是一个非常重要的机制。无论在内核对各种网络数据的接收和处理本质上是对各种内存的分配、传递和处理。LWIP中用到了内存池和内存堆这两个东东。我们来仔细看看。1 动态内存池: ...

    无论在哪一种系统中,动态内存管理都是一个非常重要的机制。无论在内核对各种网络数据的接收和处理本质上是对各种内存的分配、传递和处理。

    LWIP中用到了内存池和内存堆这两个东东。我们来仔细看看。

    1 动态内存池:

            动态内存池分配策略可以说是一个比较笨的分配策略了,但其分配策略实现简单,内存的分配、释放效率高,可以有效防止内存碎片的产生。这种方式下,用户只能申请大小固定的空间。在LWIP中,这种方式主要用于内核中固定数据结构的分配。例如UDP控制块、TCP控制块等,内核在初始化的时候已经为每个数据结构类型都初始化好了一定数量的POOL,源文件memp.c和memp.h包含了实现动态内存池分配策略的所有数据结构和函数。


    系统在初始化时,会事先在内存中初始化相应的空闲内存空间,如上图所示,系统将所有可用区域以固定的大小为单位进行划分,然后用一个简单的链表将所有空闲块连接起来。由于链表中所有节点的大小相同,所以分配时不需要查找,直接取出第一个节点中的空间分配给用户即可;

    释放时,也很简单,直接将释放的内存空间插入到对应链表首部即可。

    内存池的基本思路就是这个样子的,这种方式的内存分配用在固定的数据结构进行空间的分配,例如TCP头部、IP首部。我们来看看LWIP中的实现。

    把协议栈中所有的内存池POOL放在一起,并把他们放在一片连续的内存区域,这呈现给用户的就是一个大的缓存池。因此,缓存池的内部组织应该是这个样子的:开始处放了A类型的POOL池a个,紧接着放上B类型的POOL池b个,再接着放上C类型的POOL池c个,直到N类型的POOL池n个。

    系统中,和缓存池管理相关的全局数据变量或数据类型如下所示


    我们一个一个来讲,看LWIP是如何一个一个来实现的

    memp_t是系统定义的一个枚举数据类型,是为每一个POOL类型起了一个名字

    typedef enum {
    #define LWIP_MEMPOOL(name,num,size,desc)  MEMP_##name,
    #include "lwip/memp_std.h"
      MEMP_MAX
    } memp_t;

    lwip中对mem_t的定义是这个样子,会不会有点烧脑,这是什么鬼

    其余的那几个数据类型都是采用的类似方式,我们把mem_t搞懂,其他的也就明白了。

    c语言中“##”是连接符的意思,用来连接两个token为一个token,下一句中则包含了memp_std.h,这个文件里面都是啥呢

    #if LWIP_RAW
    LWIP_MEMPOOL(RAW_PCB,        MEMP_NUM_RAW_PCB,         sizeof(struct raw_pcb),        "RAW_PCB")
    #endif /* LWIP_RAW */
    
    #if LWIP_UDP
    LWIP_MEMPOOL(UDP_PCB,        MEMP_NUM_UDP_PCB,         sizeof(struct udp_pcb),        "UDP_PCB")
    #endif /* LWIP_UDP */
    
    #if LWIP_TCP
    LWIP_MEMPOOL(TCP_PCB,        MEMP_NUM_TCP_PCB,         sizeof(struct tcp_pcb),        "TCP_PCB")
    LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN,  sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN")
    LWIP_MEMPOOL(TCP_SEG,        MEMP_NUM_TCP_SEG,         sizeof(struct tcp_seg),        "TCP_SEG")
    #endif /* LWIP_TCP */
    
    #if IP_REASSEMBLY
    LWIP_MEMPOOL(REASSDATA,      MEMP_NUM_REASSDATA,       sizeof(struct ip_reassdata),   "REASSDATA")
    #endif /* IP_REASSEMBLY */
    #if IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
    LWIP_MEMPOOL(FRAG_PBUF,      MEMP_NUM_FRAG_PBUF,       sizeof(struct pbuf_custom_ref),"FRAG_PBUF")
    #endif /* IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */
    
    #if LWIP_NETCONN
    LWIP_MEMPOOL(NETBUF,         MEMP_NUM_NETBUF,          sizeof(struct netbuf),         "NETBUF")
    LWIP_MEMPOOL(NETCONN,        MEMP_NUM_NETCONN,         sizeof(struct netconn),        "NETCONN")
    #endif /* LWIP_NETCONN */
    
    #if NO_SYS==0
    LWIP_MEMPOOL(TCPIP_MSG_API,  MEMP_NUM_TCPIP_MSG_API,   sizeof(struct tcpip_msg),      "TCPIP_MSG_API")
    #if !LWIP_TCPIP_CORE_LOCKING_INPUT
    LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg),      "TCPIP_MSG_INPKT")
    #endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
    #endif /* NO_SYS==0 */
    
    #if LWIP_ARP && ARP_QUEUEING
    LWIP_MEMPOOL(ARP_QUEUE,      MEMP_NUM_ARP_QUEUE,       sizeof(struct etharp_q_entry), "ARP_QUEUE")
    #endif /* LWIP_ARP && ARP_QUEUEING */
    
    #if LWIP_IGMP
    LWIP_MEMPOOL(IGMP_GROUP,     MEMP_NUM_IGMP_GROUP,      sizeof(struct igmp_group),     "IGMP_GROUP")
    #endif /* LWIP_IGMP */
    
    #if (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) /* LWIP_TIMERS */
    LWIP_MEMPOOL(SYS_TIMEOUT,    MEMP_NUM_SYS_TIMEOUT,     sizeof(struct sys_timeo),      "SYS_TIMEOUT")
    #endif /* LWIP_TIMERS */
    
    #if LWIP_SNMP
    LWIP_MEMPOOL(SNMP_ROOTNODE,  MEMP_NUM_SNMP_ROOTNODE,   sizeof(struct mib_list_rootnode), "SNMP_ROOTNODE")
    LWIP_MEMPOOL(SNMP_NODE,      MEMP_NUM_SNMP_NODE,       sizeof(struct mib_list_node),     "SNMP_NODE")
    LWIP_MEMPOOL(SNMP_VARBIND,   MEMP_NUM_SNMP_VARBIND,    sizeof(struct snmp_varbind),      "SNMP_VARBIND")
    LWIP_MEMPOOL(SNMP_VALUE,     MEMP_NUM_SNMP_VALUE,      SNMP_MAX_VALUE_SIZE,              "SNMP_VALUE")
    #endif /* LWIP_SNMP */
    #if LWIP_DNS && LWIP_SOCKET
    LWIP_MEMPOOL(NETDB,          MEMP_NUM_NETDB,           NETDB_ELEM_SIZE,               "NETDB")
    #endif /* LWIP_DNS && LWIP_SOCKET */
    #if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC
    LWIP_MEMPOOL(LOCALHOSTLIST,  MEMP_NUM_LOCALHOSTLIST,   LOCALHOSTLIST_ELEM_SIZE,       "LOCALHOSTLIST")
    #endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
    #if PPP_SUPPORT && PPPOE_SUPPORT
    LWIP_MEMPOOL(PPPOE_IF,      MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc),    "PPPOE_IF")
    #endif /* PPP_SUPPORT && PPPOE_SUPPO

    memp_std.h文件中有很多形如LWIP_MEMPOOL()这样的语句,那是如何使用呢?例如

    LWIP_MEMPOOL(RAW_PCB,        MEMP_NUM_RAW_PCB,         sizeof(struct raw_pcb),        "RAW_PCB")

    对于这条语句,放到

    #define LWIP_MEMPOOL(name,num,size,desc)  MEMP_##name,

    那就是变成了MEMP_RAW_PCB,这就是这个内存池的名字。按照这种规则,编译完该头文件以后,枚举类型memp_t就定义出来了。文件memp_std.h本质上是有一个一个的LWIP_MEMPOOL宏组成的,编译器对他们以此进行替换,这样,当memp_std.h编译完成后,memp_t就建立起来了,内容如下:

    typedef num{
         RAW_PCB,
         UDP_PCB,
         TCP_PCB,
         TCP_PCB_LISTEN,
         TCP_SEG,
         .........
         MEMP_MAX
    }memp_t;

    MEMP_MAX并不代表任何类型的POOL,这里巧妙的利用枚举来表示一下内核中有多少个内存池类型。

    这个名字的作用是:在申请内存池的时候,就是以枚举中名字为依据来分配响应大小的内存空间。

    对于这个头文件注意以下几点:

    1、这个文件是会被多次编译,是因为我们在创建表中的数据类型时会多次编译这个头文件。

    2、文件的最后有一句#undef LWIP_MEMPOOL,是因为我们会使用多次这个宏,并且使用的时候宏定义的功能不是一成不变的。

    ok,明白了上述这个枚举变量的意思,其他的就都是相似的了,

    memp_sizes指向每一类POOL中单个POOL的大小

    const u16_t memp_sizes[MEMP_MAX] = {
    #define LWIP_MEMPOOL(name,num,size,desc)  LWIP_MEM_ALIGN_SIZE(size),
    #include "lwip/memp_std.h"
    };

    在该数组被编译时,宏LWIP_MEMPOOL被定义为LWIP_MEM_ALIGN_SIZE(刚才提高的这个宏会被多次使用,并且它的功能也会改变),后者是一个简单的对变量size进行操作的宏,它的作用是将size向上对MEM_ALIGNMENT取整,即保证size是内存对齐字节数的整数倍。该数组被编译后,就变成了这个样子

    const u16_t memp_sizes[MEMP_MAX] = {
     LWIP_MEM_ALIGN_SIZE(sizeof(struct raw_pcb)),
     LWIP_MEM_ALIGN_SIZE(sizeof(struct udp_pcb)),
     LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_pcb)),
     LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_pcb_listen)),
     LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_seg)),
    ........
    };

    memp_num中保存了每一类POOL中包含的POOL个数

    static const u16_t memp_num[MEMP_MAX] = {
    #define LWIP_MEMPOOL(name,num,size,desc)  (num),
    #include "lwip/memp_std.h"
    };

    直接将宏LWIP_MEMPOOL用其中的num参数代替,处理后的结果是

    static const u16_t memp_num[MEMP_MAX] = {
    (MEMP_NUM_RAW_PCB),
    (MEMP_NUM_UDP_PCB),
    (MEMP_NUM_TCP_PCB),
    (MEMP_NUM_TCP_PCB_LISTEN),
    ..........
    };


    上述数组中MEMP_NUM_RAW_PCB宏都是可以自定义的,记录了相应类型POOL的个数,用户可以在lwipopts.h设置,若没有定义,将使用opt.h中的默认配置

    memp_desc是一个指针数组,描述对应POOL

    #ifdef LWIP_DEBUG
    static const char *memp_desc[MEMP_MAX] = {
    #define LWIP_MEMPOOL(name,num,size,desc)  (desc),
    #include "lwip/memp_std.h"
    };
    #endif /* LWIP_DEBUG */

    编译后变成这个样子

    #ifdef LWIP_DEBUG
    static const char *memp_desc[MEMP_MAX] = {
    ("RAW_PCB"),
    ("TCP_PCB"),
    ("UDP_PCB"),
    ("TCP_PCB_LISTEN"),
    ("TCP_SEG"),
    .......
    };
    #endif /* LWIP_DEBUG */

    memp_memory内存池区域

    static u8_t memp_memory[MEM_ALIGNMENT - 1 
    #define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )
    #include "lwip/memp_std.h"
    ];

    这里看着有点费劲,这里把#define LWIP_MEMPOOL(name,num,size,desc)定义成+ ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )这种形式,编译完头文件后就变成了

    memp_memory[MEM_ALIGNMENT - 1+()+()+()+()+()+()+().......]这种形式了

    MEMP_SIZE表示的是需要在每个POOL头部预留的空间,这里没有用到这个功能,这项值为0.宏MEMP_ALIGN_SIZE的功能也是将size的值向上取整,以满足对齐方式的要求。

    最后还有一个全局指针数组memp_tab[],这个指针数组用于指向各个类型的内存池中的第一个空闲的POOL,就是要在分配空间的时候从这里取出,释放空间的时候从这个点插入。

    static struct memp *memp_tab[MEMP_MAX];
    struct memp {
      struct memp *next;
    
    };

    结构体memp很简单,主要是一个指针,指向下一个空闲的POOL

    2 内存池的函数实现

        与内存池相关的系统函数有3个;1、内存池初始化函数memp_init, 2、内存池分配函数memp_malloc. 3、内存池释放函数memp_free

    void
    memp_init(void)
    {
      struct memp *memp;
      u16_t i, j;
    
    
    
    
      memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory);            //先将内存池起始空间对齐
    
      /* for every pool: */                                         //依次对所有类型的内存POOL进行操作
      for (i = 0; i < MEMP_MAX; ++i) {
        memp_tab[i] = NULL;                                         //初始指针为NULL
                  
        /* create a linked list of memp elements */
        for (j = 0; j < memp_num[i]; ++j) {                        //将同类型的POOL通过链表连接到一起
          memp->next = memp_tab[i];                                //采用头插入的方法
          memp_tab[i] = memp;                                     //memp_tab[i]始终指向该类POOL中第一个可用的内存块
          memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]);
        }
      }
    
    }
    

    初始化完毕后,形成的内存空间如下图所示



    内存池分配

    内存池的分配挺简单,只要memp_tab[]数组中相应链表的指针不为空,就说明该类型的POOL还有空间可分配。从memp_tab[]中取出相应的地址就能使用了。

    /**
     * Get an element from a specific pool.
     *
     * @param type the pool to get an element from
     *
     * the debug version has two more parameters:
     * @param file file name calling this function
     * @param line number of line where this function is called
     *
     * @return a pointer to the allocated memory or a NULL pointer on error
     */
    void * memp_malloc(memp_t type)                         //输入参数type就是需要分配的POOL的类型
    
    {
      struct memp *memp;
      SYS_ARCH_DECL_PROTECT(old_level);                     //声明一个临界区保护变量
     
      SYS_ARCH_PROTECT(old_level);                          //进入临界区
    
      memp = memp_tab[type];                                //获取对应头指针指向的POLL
      
      if (memp != NULL) {
        memp_tab[type] = memp->next;                        //头指针移动,指向下一个节点
    
        MEMP_STATS_INC_USED(used, type);                     
    
        memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE);
      } else {
      
        MEMP_STATS_INC(err, type);
      }
    
      SYS_ARCH_UNPROTECT(old_level);                          //退出保护
    
      return memp;                                            //返回可用空间起始地址
    }
    

    内存池的释放也很简单,把要释放的空间插入到相应POOL的头部

    /**
     * Put an element back into its pool.
     *
     * @param type the pool where to put mem
     * @param mem the memp element to free
     */
    void
    memp_free(memp_t type, void *mem)
    {
      struct memp *memp;
      SYS_ARCH_DECL_PROTECT(old_level);                    //声明临界区变量
    
      if (mem == NULL) {
        return;
      }
    
    
      memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);
    
      SYS_ARCH_PROTECT(old_level);
    
    
      MEMP_STATS_DEC(used, type); 
      
      memp->next = memp_tab[type];                //将这个POOL插入到memp_tab头部
      memp_tab[type] = memp;
    
    
    
      SYS_ARCH_UNPROTECT(old_level);               //退出临界区
    }
    





    展开全文
  • 堆的核心概述一个JVM实例只存在一个堆内存,堆也是java内存管理的核心区域Java堆区jvm启动的时候被创建,其空间大小也就确定了。是jvm管理的最大一块内存空间。(堆内存大小可以调节)《java虚拟机规范》规定,堆...

    堆的核心概述

    一个JVM实例只存在一个堆内存,堆也是java内存管理的核心区域

    Java堆区在jvm启动的时候被创建,其空间大小也就确定了。是jvm管理的最大一块内存空间。(堆内存的大小可以调节)

    《java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的

    所有的线程共享java堆,在这里还可以划分线程私有的缓冲区

    《Java虚拟机规范》中对Java堆的描述是:所有对象实例以及数组都应该运行时分配在堆上

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

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

    堆是GC(Garbage Collection)执行垃圾回收的重点区域

    内存细分

    现代垃圾收集器大部分都基于分代收集理论设计,堆空间分为:

    java7之前堆内存逻辑上分为三部分:新生区+养老区+永久区

    java8之后堆内存逻辑上分为三部分:新生区+养老区+元空间

    设置堆内存大小与OOM

    设置堆空间大小的参数

    设置堆空间大小的参数

    -Xms 用来设置堆空间(年轻代+老年代)的初始内存大小

    -X 是jvm的运行参数

    ms 是memory start

    -Xmx 用来设置堆空间(年轻代+老年代)的最大内存大小

    默认堆空间的大小

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

    最大内存大小:物理电脑内存大小 / 4

    手动设置:-Xms600m -Xmx600m

    开发中建议将初始堆内存和最大的堆内存设置成相同的值。

    查看设置的参数:方式一: jps / jstat -gc 进程id

    方式二:-XX:+PrintGCDetails

    OutOfMemory举例

    public class OOMTest {

    public static void main(String[] args) {

    ArrayList list = new ArrayList<>();

    while(true){

    try {

    Thread.sleep(20);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    list.add(new Picture(new Random().nextInt(1024 * 1024)));

    }

    }

    }

    class Picture{

    private byte[] pixels;

    public Picture(int length) {

    this.pixels = new byte[length];

    }

    }

    //Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

    年轻代与老年代

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

    一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速

    另外一类对象的生命周期却非常短,在某些极端的情况下还能够与JVM的生命周期保持一致

    java堆区进一步细分的话,可以分为年轻代和老年代

    其中年轻代又可以划分为Eden空间,Survivor0和Survivor1空间(也叫from,to区)

    88438ecbd8c8a799f1db35ad97a3924a.png

    f054d41b397d42535c33a959d1996c68.png

    配置新生代与老年代在堆结构的占比

    默认-XX:NewRation=2,表示新生代占1,老年代占2,新生代占整个堆的1/3

    在HotSpot中,Eden空间和另外两个Survivor空间大小所占比例为8:1:1

    可以通过-XX:SurvivorRatio调整空间比例

    几乎所有的Java对象都是在Eden区被new出来的

    绝大部分的Java对象的销毁都在新生代进行了

    可以通过-Xmn设置新生代的最大内存大小

    对象分配过程

    概述:

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

    new的对象先伊甸园(Eden)。此区有大小限制

    当伊甸园的空间填满时,程序有需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不在被其他对象所引用的对象进行销毁。在加载新的对象放到伊甸园区

    然后将伊甸园中的剩余对象移动到幸存者0区

    如果再次触发垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区

    什么时候进入养老区?可以设置次数。默认为15

    可以设置参数:-XX:MaxTenuringThreshold=进行设置

    318f4166cb6a38911bdfec0c00b2bcd2.png

    注意事项

    survivor区满了不会进行垃圾回收,而是在伊甸园区满了之后垃圾回收算法对伊甸园区进行回收的同时,survivor区会被动的进行垃圾回收

    总结

    针对幸存者S0,S1区的总结:复制之后有交换,谁空谁是to

    关于垃圾回收:频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间收集

    32f2e94161f7d0fa6a4282786417a36b.png

    Minor GC,Major GC,Full GC概念

    JVM在进行GC时,并非每次都对上面三个内存(新生代,老年代,方法区)区域一起回收的,大部分的时候回收都是指新生代

    针对Hotspot VM的实现,它里面的GC按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆收集(FULL GC)

    部分收集:不是完整收集java堆的垃圾收集。其中又分为:

    新生代收集(Minor GC/Young GC):只是新生代(Eden/S0,S1)的垃圾收集

    老年代收集(Major GC/Old GC):只是老年代的垃圾收集

    只有CMS GC会有单独收集老年代的行为

    注意:很多时候Major GC会和FULL GC混淆使用,需要具体分辨是老年代回收,还是整堆回收

    混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集

    目前只有G1 GC会有这种行为

    整堆收集(FULL GC):收集整个java堆和方法区的垃圾收集

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

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

    因为Java对象大多都是朝生熄灭的特征,所以Minor GC非常频繁,一般回收速度比较快。

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

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

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

    出现了Major GC,经常会伴随至少一.次的Minor GC (但非绝对的,在ParallelScavenge收集器的收集策略里就有直接进行MajorGC的策略选择过程)

    也就是在老年代空间不足时,会先尝试触发Minor GC。 如果之后空间还不足,则触发Major GC

    Major GC的速度一般会比Minor Gc慢10倍以上,STW的时间更长。

    如果Major GC后,内存还不足,就报00M了。

    Fu11 GC触发机制

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

    调用System. gc()时,系统建议执行Full GC,但是不必然执行

    老年代空间不足

    方法区空间不足

    通过Minor GC后进入老年代的平均大小大于老年代的可用内存

    由Eden区、survivor space0 (From Space) 区向survivor space1 (ToSpace) 区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

    说明: full gc是开发或调优中尽量要避免的。这样暂时时间会短一些。

    堆空间分代思想

    为什么要把java堆分代?

    经研究表明:不同对象的生命周期不同。70%-99%的对象都是临时对象

    新生代:有Eden,两块大小相同的Survivor(from/to或S0/S1)构成,其中to总为空

    不分代能正常工作吗?

    其实不分代完全可以,分代的唯一理由就是优化GC性能。如果没有分代,那所有的对象都在一块,就如同把一个学校的人都关在一个教室。GC的时候要找到哪些对象没用这样就会对堆的所有区域进行扫描。而很多对象都是朝生夕死的,如果分代的话,把新创建的对象放到某一地方,当GC的时候先把这块存储“朝生夕死”对象的区域进行回收,这样就会腾出很大的空间出来。

    内存分配策略

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

    针对不同年龄段的对象分配原则如下所示:

    优先分配到Eden

    大对象直接分配到老年代

    尽量避免程序中出现过多的大对象

    长期存活的对象分配到老年代

    动态对象年龄判断

    如果Survivor 区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等到MaxTenur ingThreshold中要求的年龄。

    空间分配担保

    -XX:HandlePromotionFailure

    对象分配内存:TLAB(Thread Local Allocation Buffer)

    堆区是线程共享的区域,任何线程都可以访问到堆区中的共享数据

    由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的

    为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度

    什么是TLAB

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

    多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为快速分配策略

    fff0a77c3239631ff8b8cbad06bcbb94.png

    尽管不是所有的对象实例都能够在TLAB中成功分配内存,但JVM确实是将TLAB作为内存分配的首选。

    在程序中,开发人员可以通过选项“-XX :UseTLAB”设置是否开启TLAB空间。

    默认情况下,TLAB空间的内存非常小,占有整个Eden空间的1%,当然我们可以通过选项“-XX:TLABWasteTargetPercent”设置TLAB空间所占用Eden空间的百分比大小。

    一旦对象在TLAB空间分配内存失败时,JVM就会尝试着通过使用加锁机制确保数据操作的原子性,从而直接在Eden空间中分配内存。

    分配过程

    3e7d32513794d19f45288349790407fb.png

    堆空间的参数配置

    测试堆空间常用的jvm参数:

    -XX:+PrintFlagsInitial : 查看所有的参数的默认初始值

    -XX:+PrintFlagsFinal :查看所有的参数的最终值(可能会存在修改,不再是初始值)

    具体查看某个参数的指令: jps:查看当前运行中的进程

    jinfo -flag SurvivorRatio 进程id

    -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:HandlePromotionFailure:是否设置空间分配担保

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

    如果大于,则此次Minor GC是安全的

    如果小于,则虚拟机会查看-XX: HandlePromotionFailure设置值是否允许担保失败。

    如果HandlePromotionFailure=true, 那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小。

    如果大于,则尝试进行一次Minor GC,但这次Minor GC依然是有风险的;

    如果小于,则改为进行一-次Full GC。

    如果HandlePromotionFailure=false, 则改为进行一次Full GC。

    在JDK6 Update24之 后,HandlePromotionFailure参数不会再影响到虚拟机的空间分配担保策略,观察OpenJDK中的源码变化,虽然源码中还定义了

    HandlePromotionFailure参数,但是在代码中已经不会再使用它。JDK6 Update24之后的规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。

    分配对象的选择

    在《深入理解Java虚拟机》中关于Java堆内存有这样一段描述:随着JIT编译期的发展与逃逸分析技术逐渐成熟,栈上分配,标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么绝对了

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

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

    逃逸分析概述

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

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

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

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

    当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸

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

    参数设置:

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

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

    选项“-XX: fDoEscapeAnalysis"显式开启逃逸分析

    通过选项“-XX: +PrintEscapeAnalysis" 查看逃逸分析的筛选结果。

    代码优化

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

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

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

    栈上分配

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

    常见的栈上分配场景:给成员变量赋值,方法返回值,实例引用传递

    同步省略

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

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

    public void f(){

    Object hollis = new Object();

    synchronized(hollis){

    System.out.print(hollis)

    }

    }

    代码中hollis这个对象进行加锁,但是hollis对象的生命周期只在f()方法中,并不会被其他线程所访问,所以在JIT编译阶段就会被优化掉。优化成:

    public void f(){

    Object hollis = new Object();

    System.out.print(hollis)

    }

    标量替换

    标量(Scalar)是指一个无法在分解成更小的数据的数据。Java中的原始数据类型就是标重.

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

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

    public static void main(String[] args) {

    alloc();

    }

    private static void alloc(){

    Point point = new Point(1,2);

    }

    class Point{

    private int x;

    private int y;

    public Point(int x,int y){

    this.x = x;

    this.y = y;

    }

    }

    以上代码经过标量替换后就会变成

    private static void alloc(){

    int x = 1;

    int y = 2;

    }

    可以看到,Point这个聚合量经过逃逸分析后,发现他并没有逃逸,就被替换成两个聚合量了。

    标量替换的好处:可以大大减少堆内存的占用,因为一旦不需要创建了,那么就不需要分配堆内存了

    标量替换参数

    -XX:+EliminateAllocations:开启了标量替换(默认打开),允许对象打散分配在栈上

    展开全文
  • 这两天公众号,学习了一个知识点,Java对象并不是都会内存中分配空间的。之前写了一篇比较长的关于JVM学习的笔记,里面说过,Java创建对象实例的时候,大部分新生对象都是存放内存Eden区中的,少数情况下...
  • 我的意思是我理解什么是栈,但是它们到底是什么,哪儿呢(站实际的计算机物理内存的角度上)? 通常情况下由操作系统(OS)和语言的运行时(runtime)控制吗?它们的作用范围是什么?它们的大小由什么...
  • 一直以来都跑cuda程序,用的显卡都是P4000,但P4000的性能和我预想中的差距实在是有些大,哎,不...显存大小从显卡参数就能出来,这个不详细说了。带来的影响,其实和内存对于CPU一样的:多了没用,少了卡成狗。...
  • 最近在巩固算法,这个先做笔记了,有空再精细研究。 ...知道可以动态申请堆空间,堆空间是系统维护的内存块链表,用户申请时,系统找到合适大小的...大小什么的都记录在哪了一个自实现,想明白一些了:
  • 如果有多台服务器,这时需要安装一些新应用,那么装在哪台设备上更合适呢?这是我的第一个想法是这些服务器的剩余空间和剩余内存。 那么怎么才能硬盘空间及内存呢? 查看剩余内存 free -m free命令可以...
  • 我的意思是我理解什么是栈,但是它们到底是什么,哪儿呢(站实际的计算机物理内存的角度上)? 1. 通常情况下由操作系统(OS)和语言的运行时(runtime)控制吗? 2. 它们的作用范围是什么? 3. 它们...
  • /*DEV C++编译有问题,但CodeBlocks编译没问题而且能运行,但是运行时又数据测试删除结点后程序运行异常结束*/ #include <stdio.h> #include <stdlib.h> #include <string.h> #define M 100+1 // 宏定义...
  • lwbt的内存分配详解

    千次阅读 2012-10-17 18:17:28
    这个数组中规定了一段是属于哪个类型的,这样做的方法不是很科学,是通过规定各个类型结构的最大能用的个数来取的。Hci_pcb数组中的元素是每个类型的大小。memp_tab首先来下memp数组中的元素都是这个结构的...
  • 前段时间,上游系统给我生成了一个文件,200多个G,我这8G内存的小心脏能承受的起,当场休克。 然后我痛定思痛,我决定要对他进行制裁,校验他生成的文件,超过1个G的我就不读了,直接抛异常给他。 然后我...
  • 华为畅享10S与畅享10Plus配置上的差异,主要体现屏幕、内存搭配、机身大小等方面。屏幕畅享10S采用了一块6.3英寸、OLED材质的水滴屏,分辨率为2400×1080,像素密度为417PPI,并支持屏下指纹解锁;畅享10Plus采用...
  • 使用flash存储数据的时候,对于新手来说,并不知道该把数据存在一个地址上,怕存到程序区域,或者越界。关于这一点很容易搞清楚,首先我们需要我们编译出的程序有多大。上图可以计算出程序的大小,bin=Code+RO...
  • 目标:Linux系统下用QT实现界面整体缩放。... 但是我们老大说这个占用内存太多,用来运行程序的那块板子可能会带不动,不知道有没有位能帮我看看可以从哪些方面优化一下,降低我这个程序对内存的占用。
  • 用于运算指令执行后,存储运算结果的某些状态。从程序员的角度硬件CPU:种类,时钟信号的频率。可以使用种机器语言取决于CPU的种类。内存信息:地址空间,每个地址中可以存储多少比特的信息。每个地址都标志着...
  • 项目的开发过程中遇到了一个奇怪的现象,就是任务管理器中看java进程没有占用太多的内存,但是,其中的一个线程内存溢出了,java.lang.outofmemory java heap space 这是为什么呀 我重新调整了虚拟机的堆大小,...
  • 那么甚至需要大家连补丁都一样),或是dll的和exe都由用户自己同一个编译器编译,或是直接把dll的代码包含exe中,不使用dll了, 否则,内存中数据必然不一样, 不可能让exe正常调用这个dll,到时出现各种无理头问题,又找不...
  • 可是它们究竟是什么,哪儿呢(站实际的计算机物理内存的角度上)? 通常情况下由操作系统(OS)和语言的执行时(runtime)控制吗?它们的作用范围是什么?它们的大小由什么决定?...
  • java对象的创建过程

    2020-05-10 23:42:57
    给对象分配内存空间的过程其实就是相当于java堆内存中划分出一块相同大小(对象所需的内存大小)的内存出来; 而分配内存有两种方式: 选择种方式由java堆内存是否规整(即已使用过的内存和未使用的内存放在两
  • -修正Form不能自适应浏览器大小的改变(feedback:kaywood)(WorkItem#6309)。 -增加重载方法Alert.Show(message, title, icon)(feedback:TheBox)(WorkItem#6353)。 -为容器控件(比如Panel,Region,Tab等)增加AJAX...
  • 到底什么时候会尝试Minor GC? 新生代填满到90%的时候: 同时,说出为什么新生代是 8...如果有则进行下一步尝试,看看老年代的内存大小,是否大于之前每一次MinorGC后进入老年代的对象平均大小; 什么时候Mino...
  • 透过程序了解命令pmap内mapping的含义

    千次阅读 2015-03-18 15:12:47
    这几天了解进程各个段的所占内存大小的时候,碰到pmap这个命令,但是从网上查了一下,还是不明白mapping那一列annon 等都代表什么含义. 于是通过特定的程序来看看他们所代表的含义,主要原理就是根据变量所在段的不同...
  • 今天,站长群里面看到小伙伴聊WordPress缓存加速的问题,于是就好奇撸了两眼。...Memecache将所有数据存储内存中,内存会断电后挂起,并且数据不能超过内存大小。 Redis的一部分存在于硬盘上,可以
  • MyISAM和InnoDB的区别

    2020-12-16 23:21:31
    因此对内存要求较高,内存大小对性能有决定性的影响 表空间比较大 关注的点 事务 默认安装的 查看种引擎的命令 你的mysql现在已提供什么存储引擎: mysql> show engines; 你的mysql当前默认的存储...
  • 下面看看实际应用中,顺序表和链表应该选择哪个。 基于空间的考虑:  内存分配问题:顺序表属于静态分配,必须实现知道所需的内存空间大小。但是,如果分配内存过大,将造成空间的浪费,如果分配过小,...
  • Redis持久化过程一直是影响redis性能的常见因素,如何监控持久化以及如何优化持久化...阻塞时间和redis数据占用的内存大小成正比关系,每1G内存fork耗时20毫秒。 如想知道fork阶段的阻塞时间,可以使用info stat.
  • 觉得很奇怪,我确认源是有内容的,但就是执行后没返回,然后就一步一步输出信息,看看是脚本中一步出现问题。但是很奇怪,调试过程中,内容时有输出。我怀疑是不是php分配的内存大小不够,然后果断扩大memory_...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 151
精华内容 60
关键字:

在哪看内存大小