wow占用物理内存随时间微信占用内存越来越大大,最后导...

后使用快捷导航没有帐号?
查看: 1081|回复: 1
新人欢迎积分0 阅读权限30积分122精华0UID帖子金钱539 威望0
Lv.3, 积分 122, 距离下一级还需 128 积分
UID帖子威望0 多玩草0 草
听说人物边角多七倍的。。。
新人欢迎积分1 阅读权限40积分419精华0UID帖子金钱1382 威望0
Lv.4, 积分 419, 距离下一级还需 581 积分
UID帖子威望0 多玩草0 草
没抢过沙发&&那你这帖子开刀
需要金钱:1100
Powered by
手机盒子客户端点击或扫描下载剑网3 内存占用越来越高_百度知道谈大容量内存的各种使用方案对磁盘效率影响
点击数:77651|回复数:282
本帖最后由 jeffxl 于
22:17 编辑
以下内容为JEFFXL原创内容,欢迎转载并注明出处
& && &&&在内存价格很低的今天,现在很多用户都配置了大容量的内存(8G或以上)。也有很多低内存(4G或4G以下)的朋友想通过升级内存或者SSD来提高IO操作体验。但是,我发现关于内存和SSD对家庭用户的IO操作体验上有这样或那样的误区。比如,很多人使用EWF系统、各种软件缓存系统,各种RAM盘技术,想借此来提升IO操作体验。经费紧张的用户还有的陷入加大容量内存还是上SSD的纠结之中。以下我来说说各种方案和各种应用场合,大内存容量和SSD对用途体验在实际应用中究竟产生什么体验上的区别。
& && &&&关于用户软件对内存的利用:
& && &&&一般我们知道,32位操作系统(只谈NT核心,暂不谈PAE技术)的寻址能力是4GiB,NT核心的操作系统在管理物理内存时在用户层进程上做出了一定的限制。首先,各种硬件和驱动需要单独寻址空间,这个部分按硬件和硬件数量的不同而有些差异,一般32位系统除开硬件寻址占用后最大全局可用容量大部分为3.25G-3.9G之间,这里的占用为硬性占用,无法在用户层面做出任何调整。而操作系统对内存的管理又分为系统级(RING0)和用户级(RING3)两个部分。系统自身保留最大2GiB寻址范围作为系统级调用使用,又称为内核模式地址空间。而用户级应用被分配剩下的2GiB寻址范围作为可用地址,又称为用户模式虚拟地址空间。那么,软件总共可用内存地址范围为2GiB,用户层软件单进程最多可以申请2GiB内存(32位保护模式,每进程在隔离的“虚拟”映射地址当中寻址,进程间互不干扰),通过 boot.ini中 /3gb 参数的调整可以达成用户模式使用3GiB的内存容量,而系统只保留1GiB作为进程地址空间。调整boot.ini中 /3gb 参数可能会导致一些兼容性问题,PAE扩展寻址到36位也是同样,但用户模式还是2GiB或3GiB限制,只不过可以同时开启多个占用2GiB内存的应用或者提高系统缓存效率,而无需使用页面交换。NT6核心以后的系统请使用命令行键入bcdedit /set IncreaseUserVa 3072来配置用户模式可用内存到3GiB(使用2048参数来还原)。
注:微软知识库关于内存使用和 /3GB 开关的知识库文章
& && &&&X64环境下,对于 Microsoft Windows 操作系统,为了避免内存页表太大,目前可用的寻址范围实际为44位地址,也就是16TiB,并不能达成全部64位寻址范围的16 Exabyte。这里有一个问题,现在家庭用户99%还是使用32位软件进程来跑应用,上64位系统只是为了提供更大的寻址范围支持更多的内存,那么能够使用这么多内存吗?目前WOW64对32位程序基本完美兼容,但是在跑在32位兼容模式下的应用同样需要遵守32位用户模式寻址范围的限制,也就是2GiB或者通过牺牲兼容性调整到3GiB。那么如果在没有使用原生64位程序时,不多开N个大型应用软件的话(每进程最大可提交2GiB内存),那么大容量内存很显然没有得到充分的利用。
& && &&&WIN7缓存机制:
& && &&&那么在X64环境下使用Microsoft Windows 操作系统,在已有大容量内存的条件下又没有开启多个32位大型软件的环境下,大内存就没有任何用武之地吗?显然不是,如WIN7 X64操作系统自身提供了一套动态缓存机制,会尽量使用最大的“可用&内存容量来缓存用户软件数据并使用Superfetch技术预加载经过统计的用户“热”数据,未被系统、用户程序和缓存占用的为“空闲”容量。这里的“空闲”和“可用”容量并不是同一个意思,大家可以在WIN7的任务管理器的性能标签页内找到。
未命名1.jpg (5.14 KB, 下载次数: 178)
19:22 上传
WIN7任务管理器,“物理内存部分”
可用内存容量等于总内存容量减去系统和软件占用的容量(大约)
空闲内存容量等于可用容量减去已缓存资源的容量(大约)
& && & WIN7会动态管理缓存大小,而且是全局的机制,覆盖所有存储卷的内容。WIN7缓存机制是由最大“可用”容量来决定“空闲”容量的缓存占用(有点绕口);所有未被系统和软件占用的“可用”容量都可以被用来缓存,都是“空闲”容量;随着缓存的慢慢载入,“空闲”容量会慢慢变少,而软件可申请使用的“可用”内存容量不变,被占用“空闲”容量的缓存部分随时可以因为应用软件的需要而随时释放。这里既提供了应用随时可用的“可用”空间来运行程序,又最大效率的利用了“空闲”空间来缓存资源。这个时候,大内存就起到了加速IO操作的用途。在这里,如果提供足够的“空闲”容量,那么WIN7的缓存机制几乎可以抵消大约90%以上的IO压力,使磁盘IO压力得到极大的释放,这也是当前SSD在大内存容量,X64环境下,有的用户觉得SSD对IO体验的提升不太明显的最大因素,因为操作系统自身的缓存机制已经充当了最大的IO压力缓冲区。相反小容量内存用户升级SSD反而会得到体验上很大的不同,这个问题相对看待,其实只是因为内存使用溢出而频繁使用页面交换(虚拟内存)导致机械盘非常不擅长这样的场合,而SSD在4K性能上比机械盘好太多的原因(页面文件操作几乎为纯4K的IO)
& && &&&关于大内存环境下使用EWF系统、各种软件缓存系统,各种RAM盘技术对磁盘效率的影响:
& && &&&X64 Microsoft Windows 操作系统环境下,使用各种RAM作为数据缓冲区的技术的各种方案,虽然可以提供一定的IO操作提升,但是在WIN7 X64环境下,这些软件有一定的局限性和增加软件环境复杂性的问题,可能产生一些未知的易用性问题。在家用领域,这些软件方案不一定能达成比WIN7 X64自身缓存机制在大容量内存支持下更好的性能,WIN7缓存机制篇章已经做出了说明。
& && & 先谈第三方缓存软件部分:
& && & 这样的软件一般给出了定向的设置范围和固定的内存提交方式(容量占用固定),操作方式固定且有需求范围局限性,用户操作行为的不可预知性提供了不同的缓存命中率(看使用的这些应用是集中还是离散的),且缓存命中率的不同提供了不同的性能增益,效能有不确定性。而这个操作方式相对目前家庭用户来说并不比WIN7自身的缓存机制的管理方式优秀,并不适合在WIN7下部署这样的第三方缓存软件。这里需要谈到一个数据命中率聚合性分布的问题。在家庭用户当中,在一次开机和关机的工作循环里(因为内存是易失性存储),各种缓存机制按基于数据访问热度的算法提供了软件二次加载加速和一定的热数据命中率来提供IO性能增益。
& && & 我不认为WIN7自身已有的缓存机制在典型家庭用户上会提供低于任何第三方缓存机制的效率。因为WIN7缓存机制的普适性,一般测评软件会避开WIN7的缓存机制,所以在测评上得不到好的成绩,而第三方缓存软件会“骗过”测评软件,所以可以达成很高的“分数”,而这个分数带来的体验,在数据二次加载上得不到比WIN7自身缓存机制更好的效率,WIN7自身的缓存机制是动态利用最大可用内存动态管理的,比第三方软件效率来得高,而且在需要时应用可以随时申请剩余的内存,缓存部分自动减少。而第三方应用是申请固定内存对指定目标进行缓存,损失了灵活性和普适性。
& && &&&这个部分,WIN7如我上面说明的那样,提供了更好的灵活性和普适性。额外增加第三方缓存软件增加了软件环境复杂度和管理上的复杂度,并不能提供更适合家庭用户的缓存机制。因为家庭用户一般连续开机工作循环时间不长,而一次工作循环需要载入的数据在逻辑分布上的数据总容量上远超过内存容量,重复访问性平均又不高。如果需要达成全局最高的命中率,需要很大的内存(按用户应用软件使用的分布),而如果命中率过低,缓存机制效能就得不到很好的发挥。 在这里,从容量价格比相对更便宜的磁盘系统上做出改善,得到的体验远大于部署这类第三方缓存软件。
& && &&&在部署SSD后,热数据可以按需人工分布在SSD上,因为同容量的SSD比内存便宜,而内存缓存机制又不能覆盖所有家庭用户的数据访问,有缓存未命中风险(家庭用户定向缓存所有可能访问的内容需要的内存容量太大,没性价比)。而剩下的未被缓存命中的应用程序IO压力可以通过SSD提供的IOPS来达成更好的IO操作体验(就算WIN7缓存平均承担90%,也有10%未命中)。HDD体验不佳的地方,就是WIN7缓存机制未能缓存命中时产生的,比如初次载入程序或冷数据的载入。所以家庭用户不能纯依赖大容量内存来提供等效的IO体验。
& && && &Windows操作系统的缓存系统是基于全局进行缓存,即对所有数据进行缓存,而第三方软件缓存可以根据用户需要,设置仅对某一特定分区或磁盘进行缓存。在同等缓存容量下,后者更具目标性,从而对定向目标提高了缓存命中率。
& && &&&拿无盘网吧的无盘系统镜像服务器举例,99%的访问是定向可控的。多用户启动和运行XP无盘工作站系统的过程所使用的数据经过统计,在范围和容量上趋向固定的550M左右的特定数据。这里提供第三方缓存软件并在服务器上锁定缓存内容(只定向缓存无盘系统镜像分区),只需要稍微大于550M的缓存容量就可以为工作站操作系统读部分提供接近100%的读命中。因为无盘服务器一般是连续运行的,单次启动工作站时需要的操作系统部分数据和保证平时运行的系统数据(大约550M)就会被服务器缓存,而后只要服务器连续开机状态下,其他工作站的启动过程和操作系统运行时需要的数据全部都几乎可以通过服务器内存来并发读取,提供了接近纯内存的并发性能。这里的无盘服务器指的是狭义的只提供操作系统启动和运行部分的无盘系统镜像服务器,应用内容由ISCSC架构提供(应用访问部分甚至需要SSD做2级缓存)。类似这个方案对第三方缓存系统才具有最大的使用效能,目的是为了锁定缓存范围和缓存高命中率。
& && &&&无盘服务器和单用户使用的区别就在于,在单用户(比如家庭用户)本地看来,单次工作循环中读分布几乎是全局离散的(用户行为无法预知),需要本地全局缓存,数据模型聚合性相对并不高,在单次工作循环中大部分访问重复性低,本地缓存重启则丢失,需要多个工作循环的热数据统计配合预加载技术来保证缓存效率。而N个这样的单用户组成的多用户群组,在无盘服务器端看起来就有了相当大的数据访问聚合性,所以这里的区别是相当大的。比如可以至少分离出多用户的操作系统启动和运行所需要的基本数据模型是完全一致的。而多用户应用访问上也可以而统计出这样的趋同性,比如无盘网吧的游戏访问热点局限在少数热门游戏上。统计学上的数据模型聚合性和一致性则是定向目标缓存系统高效率、高命中的最基本保证。
& && & 再谈各种RAM盘技术和系统再封装,内存回写技术:
& && & 这里包含但不限于各种生成RAM磁盘后迁移浏览器缓存文件夹、各种临时文件的环境变量到RAM盘、EWF系统、虚拟机镜像读,由内存回写的技术。这个部分,提供的IOPS增益产生了一个用户体验瓶颈的问题。家庭用户如果部署了SSD,在已有的系统自身缓存机制下,加上SSD补充了系统缓存未命中的部分承担剩下的IOPS压力,已经提供了很好的IO操作体验(原因如前文)。而再累加这些技术方案,并不能在用户体验上得到更好的附加增益。而由此带来比第三方软件缓存技术更复杂的操作管理性问题和可能的诸多易用性问题,这里我认为是得不偿失的。而在HDD环境下,如果没有特殊需求,则也不推荐使用这些应用方案。
& && &&&额外补充一个关于大内存环境下虚拟内存是否需要禁用的问题。这里我不建议完全关闭虚拟内存,有部分软件和系统操作必须使用到页面文件交换,为了兼容性考虑,请至少保留1G左右的页面文件来保证最大的系统和软件兼容性,而且这样做并不会降低任何IO性能。
& && && &总结:
& && && &WIN7的缓存机制本身就是为单用户的数据访问模型而设计,家用环境需要非常好的普适性和易用性,如全局缓存、无需用户干预、全局透明、自动管理。NT6系列系统,除了有热数据命中率导向算法的常规方式缓存外,还有由Superfetch提供的缓存预加载技术很好的统计和学习了用户的行为习惯,很好的融合了易失性内存缓存机制,提供了一定的数据初次加载加速的可能性,又因为是可动态管理的全局缓存,缓存的占用对应用程序内存申请完全透明,对用户完全透明,所以WIN7缓存机制是最适合家庭用户环境的智能缓存技术。
& && &&&除非需要保证指定的特殊目标极高IOPS的场合(比如为指定目标提供内存的IOPS能力),大内存用户在WIN7 X64环境下一般不建议使用任何第三方缓存软件和各种RAM盘和系统封装,内存回写技术。得不偿失,损失了全局缓存效能,增加了软件环境复杂度、降低可靠性和易用性。
& && &&&首先,升级SSD可以在任何环境下提供全面的IO效能提升;如果需要更佳的应用程序IO操作速度,那么增加内存并由WIN7自动管理可用内存作为动态缓存也是一个好办法,空着不用也是一种WIN7的内存“用法”
活跃度 +45
width:100%">
这个第三种是指用ramdisk这样子的软件模式吗?
width:100%">
办公就配ssd,游戏机就配大内存。有钱就都配了
width:100%">
很好,我一直认为,使用win7系统的HDD,打开过一次程序后,再次打开的速度堪比SSD的主系统,这也是我两台电脑上进入系统长久后操作感差不多的原因
win7的缓存机制起了很大作用.我发现操作感差不多后,果断删除了一切ramdisk,cache类的软件,空出所有内存让win7自由分配
width:100%">
很好的分析!
width:100%">
支持一下了
width:100%">
禁用虚拟内存的时候系统只提示会不保存错误日志 会导致兼容问题是因为你的内存还不够大 如果确定物理内存够大 关闭也不会有问题
比如全开运行crysis
4G内存 禁用虚拟内存 第2张图load到一半就自动退出 打开虚拟内存 问题解决
8G内存 禁用虚拟内存 没有任何问题
感觉win7就算禁用了虚拟内存 还是在物理内存里映射了一个地址范围专门用来放页面文件
这个地球上有个软件叫 PS&
width:100%">
学到知识了,谢谢兄弟!
我曾经也在看过相关虚拟软件的评测后,纠结于使用与不使用第3方软件管理内存。
之后还是因为8G内存用在2台电脑上,一台电脑只要4G内存了,所以才没有再考虑这个事情。
楼主能说一下,我现在用WIN7 64位系统,4G内存,H67主板,2400S的话,还要不要装第3方软件呢,主要是用来办公与高清电影。
姐夫大人意思是,不用装!!!&
学到的知识要用起来
看了帖子还不明白?&
width:100%">
我欣赏能写出实在的技术贴的技术区版主
楼主啥时候给网吧服务器的无盘回写方案出个专题啊。。
另外关于内存利用也还有不少问题,比如说 Win7 有个功能是使用系统内存做虚拟显存:
Capture22.JPG (88.17 KB, 下载次数: 175)
12:33 上传
我想知道:
1、什么情况下虚拟显存会开始占用掉物理内存?是仅发生在爆显存的时候?还是说发生在接近爆显存的时候?还是说就算显存空闲很多,系统也永远会试图“回写”一份已占显存的镜像进入物理内存?对 Win7 而言,独立显存是处于物理内存更高一级的缓存体系?
2、32 位系统下,如果不用 /3gb 开关和用 /3gb 开关,分别最多识别多少显存?64 位系统下的 32 位应用程序又是什么情况?
3、对于一个 32 位应用程序,显存寻址的时候,页表是我的图中的整个 15GB?
4、虚拟显存所占据的物理内存,在任务管理器里面应该看哪个进程名?是挂名为显卡驱动还是挂名为游戏进程?看哪一列?“Memory Working Set”?
/en-us/library/windows/hardware/gg487348
你可以参考MS的官方说明。&
那个共享系统内存部分是沿用的“AGP边带寻址”技术,可以参看类似百科内容&
width:100%">
为了兼容性桌面级系统不提供PAE支持。
桌面级有提供PAE支持,但最高内存限制在4g,PAE是DEP的前提
你说的没错。此文这个对小白我是这样表达的,没有详细***清晰,32位系统/进程申请更大内存需要微软AWE的API,是基于PAE技术的。&
width:100%">
1,Win7的内存管理机制战斗力较强
2,使用第三方软件虽然跑分惊人,但却降低了系统的可维护性
3,家用机用途广泛,IO操作离散型极强,内存缓冲命中率有限
1+2+3=普通人用大内存,内存管理交给Win7;追求体验的用户使用SSD,并将经常读取的文件置于其中;第三方软件对普通人来说意思不大。
width:100%">
还有个问题,如果是vista系统的内存系统也是如此吗?用ramdisk有效果没
vista系统的游戏兼容性比win7好多了
width:100%">
本帖最后由 jeffxl 于
22:34 编辑
luoyu_1980 发表于
还有个问题,如果是vista系统的内存系统也是如此吗?用ramdisk有效果没
vista系统的游戏兼容性比win7好多 ...
是的,而且VISTA是个重IO负荷的操作系统。后台有大量的IO操作在并行,这个系统非常适合SSD。
举个例子,VISTA比WIN7有高得多的缓存预加载优先级,几乎在系统启动到桌面后立即使用非常高的IO流量立即填充剩余内存,直到可用内存被缓存接近填满。当时VISTA的时代,机械硬盘随机效能本身就不好,那个时候更差一些。所以感觉VISTA后台IO进程非常臃肿,在那个时代的硬件条件下变得非常不合时宜,受到大多数用户的诟病(当然还有其他易用性问题,而不只是持续的IO压力大)。
VISTA的缓存技术更甚于WIN7,可以说VISTA是一个想做到几乎无所不包,无所不用其极的系统,超越了当时的硬件能提供的条件。缓存高强度填充操作和更新操作导致当时机械盘的体验相当的差劲,起了相反的作用。而摆到现在,上了SSD后使用VISTA,那么这个缓存填充和更新的速率就可以得到SSD的IOPS支持,提供了更好的缓存效率(填充更快,更新更及时)
width:100%">
luoyu_1980 发表于
还有个问题,如果是vista系统的内存系统也是如此吗?用ramdisk有效果没
vista系统的游戏兼容性比win7好多 ...
需要注意VISTA没有TRIM支持
你怎么理解WIN7游戏兼容性没有VISTA好的?
width:100%">
windingway 发表于
1,Win7的内存管理机制战斗力较强
2,使用第三方软件虽然跑分惊人,但却降低了系统的可维护性
仅限于windows 会温到死的&&linux内存管理机制不比windows差
width:100%">
我在玩一些老游戏的时候,比如帝国时代,比如Hero3等在win7下面无法正常运行。
经常是画面错误、颜色错误等。
这两个游戏在vista下面就能正常运行。win7下面,运行hero3的时候,我再运行金山游侠的时候,win7下面,金山游侠无法正常运行,具体表现锁定数据就弹出系统,然后就是游戏关闭;在vista下面hero3和金山游侠就能正常配合运行。
很是无奈。
我的笔记本,SL400和E420S分别是vista系统和win7系统。
同用游侠 在win7x64下没有任何问题&
不是的,在不用修改器的情况下,帝国2颜色不对,hero3,经常是贴图错误,然后时不时弹出游戏...&
不用修改器的话游戏有没问题啊?若没有的话,倒不如说是游侠和WIN7不兼容可能性更大。&
width:100%">
文章太高深啊
好多东西都看不动懂
看来有太多东西需要我研究了
width:100%">
学习了,感谢版主
width:100%">
用INTEL TOOLBOX做优化后,记得第一项就是关闭Superfetch缓存预加载技术,那么,如果是SSD配6G以上大内存,是打开还是关闭Superfetch缓存预加载技术?笔记本系统是WIN7 X64
width:100%">
本帖最后由 jeffxl 于
20:10 编辑
jxflyfly7 发表于
用INTEL TOOLBOX做优化后,记得第一项就是关闭Superfetch缓存预加载技术,那么,如果是SSD配6G以上大内存, ...
我认为没有任何影响,缓存的填充对于SSD来说是非常小的负载了。而且内存的IOPS能力较好,缓存命中的话,有一些些提升,只是不明显了(因为SSD的关系)
width:100%">
& 北京绝对领域咨询有限公司WOW版本快讯
WOW精彩视频
WOW副本心得
德拉诺之王
职业攻略:┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊
职业视频:┊┊┊┊┊┊┊┊┊┊┊
专题推荐:┊┊┊┊┊┊┊┊┊┊┊
黑石铸造厂:┊┊┊┊┊┊┊┊┊┊
专业技能:┊ ┊┊┊┊┊┊┊┊┊┊┊┊┊┊
装备幻化:┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊┊进程物理内存远大于Xmx的有关问题分析,导致堆未满,但OOME - 开源软件当前位置:& &&&进程物理内存远大于Xmx的有关问题分析,导致堆未满进程物理内存远大于Xmx的有关问题分析,导致堆未满,但OOME&&网友分享于:&&浏览:0次进程物理内存远大于Xmx的问题分析,导致堆未满,但OOME
进程物理内存远大于Xmx的问题分析
转自:/blog//rssxmx/
最近经常被问到一个问题,”为什么我们系统进程占用的物理内存(Res/Rss)会远远大于设置的Xmx值”,比如Xmx设置1.7G,但是top看到的Res的值却达到了3.0G,随着进程的运行,Res的值还在递增,直到达到某个值,被OS当做bad process直接被kill掉了。
top - 16:57:47 up 73 days,
load average: 6.78, 9.68, 13.31
Tasks: 130 total,
1 running, 123 sleeping,
6 stopped,
Cpu(s): 89.9%us,
SHR S %CPU %MEM
22753 admin
17m S 192.8 52.7 151:47.59 /opt/taobao/java/bin/java -server -Xms1700m -Xmx1700m -Xmn680m -Xss256k -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseStringCache -XX:+
5:53.07 [kswapd0]
物理内存大于Xmx可能吗
先说下Xmx,这个vm配置只包括我们熟悉的新生代和老生代的最大值,不包括持久代,也不包括CodeCache,还有我们常听说的堆外内存从名字上一看也知道没有包括在内,当然还有其他内存也不会算在内等,因此理论上我们看到物理内存大于Xmx也是可能的,不过超过太多估计就可能有问题了。
物理内存和虚拟内存间的映射关系
我们知道os在内存上面的设计是花了心思的,为了让资源得到最大合理利用,在物理内存之上搞一层虚拟地址,同一台机器上每个进程可访问的虚拟地址空间大小都是一样的,为了屏蔽掉复杂的到物理内存的映射,该工作os直接做了,当需要物理内存的时候,当前虚拟地址又没有映射到物理内存上的时候,就会发生缺页中断,由内核去为之准备一块物理内存,所以即使我们分配了一块1G的虚拟内存,物理内存上不一定有一块1G的空间与之对应,那到底这块虚拟内存块到底映射了多少物理内存呢,这个我们在linux下可以通过/proc/&pid&/smaps这个文件看到,其中的Size表示虚拟内存大小,而Rss表示的是物理内存,所以从这层意义上来说和虚拟内存块对应的物理内存块不应该超过此虚拟内存块的空间范围
8dc000 rwxp :00 0
1871872 kB
1798444 kB
1798444 kB
Shared_Clean:
Shared_Dirty:
Private_Clean:
Private_Dirty:
1798444 kB
Referenced:
1798392 kB
Anonymous:
1798444 kB
AnonHugePages:
KernelPageSize:
MMUPageSize:
此次为了排查这个问题,我特地写了个简单的分析工具来分析这个问题,将连续的虚拟内存块合并做统计,一般来说连续分配的内存块还是有一定关系的,当然也不能完全肯定这种关系,得到的效果大致如下:
vs rss rss_percentage(rss/total_rss) merge_block_count
0x8dc0c9a20000
0x7faf7a4cfffa7dd9000
0x7faf50c7faf6c02a000
0x7faf6cx7faf
0x418e000000
0x7fafx7faf50c78000
0x7fafx7faf
0x7fafx7faf4ad83000
0x7fafx7faf7a4c6000
0x30c9e0ca202000
0x7fafx7faf289c7000
0x30c9a0c9c20000
0x30cax30cae83000
0x30cbe0cca16000
0x7fffa7dcfffa7e00000
0x30cca0faf21dba000
0x30cbx30cbe16000
0x30cae0cb207000
0x30cax30ca617000
0x30c9c1f000-&0x30c9f89000
0xx471be000
0x7fffa7dff000-&0x0
当然这只是一个简单的分析,如果更有价值需要我们挖掘更多的点出来,比如每个内存块是属于哪块memory pool,到底是什么地方分配的等,不过需要jvm支持(注:上面的第一条,其实就是new+old+perm对应的虚拟内存及其物理内存映射情况)。
进程满足什么条件会被os因为oom而被kill
当一个进程无故消失的时候,我们一般看/var/log/message里是否有Out of memory: Kill process关键字(如果是java进程我们先看是否有crash日志),如果有就说明是被os因为oom而被kill了:
Aug 19 08:32:38 mybank-ant kernel: : [016] java invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
Aug 19 08:32:38 mybank-ant kernel: : [022] java cpuset=/ mems_allowed=0
Aug 19 08:32:38 mybank-ant kernel: : [024] Pid: 25371, comm: java Not tainted 2.6.32-220.23.2.ali878.el6.x86_64 #1
Aug 19 08:32:38 mybank-ant kernel: : [026] Call Trace:
Aug 19 08:32:38 mybank-ant kernel: : [039]
[&ffffffff810c35e1&] ? cpuset_print_task_mems_allowed+0x91/0xb0
Aug 19 08:32:38 mybank-ant kernel: : [068]
[&ffffffff81114d70&] ? dump_header+0x90/0x1b0
Aug 19 08:32:38 mybank-ant kernel: : [074]
[&ffffffff810e1b2e&] ? __delayacct_freepages_end+0x2e/0x30
Aug 19 08:32:38 mybank-ant kernel: : [079]
[&ffffffff81213ffc&] ? security_real_capable_noaudit+0x3c/0x70
Aug 19 08:32:38 mybank-ant kernel: : [082]
[&ffffffff811151fa&] ? oom_kill_process+0x8a/0x2c0
Aug 19 08:32:38 mybank-ant kernel: : [084]
[&ffffffff&] ? select_bad_process+0xe1/0x120
Aug 19 08:32:38 mybank-ant kernel: : [087]
[&ffffffff&] ? out_of_memory+0x220/0x3c0
Aug 19 08:32:38 mybank-ant kernel: : [093]
[&ffffffff&] ? __alloc_pages_nodemask+0x899/0x930
Aug 19 08:32:38 mybank-ant kernel: : [099]
[&ffffffff81159b6a&] ? alloc_pages_current+0xaa/0x110
Aug 19 08:32:38 mybank-ant kernel: : [102]
[&ffffffff81111ea7&] ? __page_cache_alloc+0x87/0x90
Aug 19 08:32:38 mybank-ant kernel: : [105]
[&ffffffff81127f4b&] ? __do_page_cache_readahead+0xdb/0x270
Aug 19 08:32:38 mybank-ant kernel: : [108]
[&ffffffff&] ? ra_submit+0x21/0x30
Aug 19 08:32:38 mybank-ant kernel: : [110]
[&ffffffff81113e17&] ? filemap_fault+0x5b7/0x600
Aug 19 08:32:38 mybank-ant kernel: : [113]
[&ffffffff8113ca64&] ? __do_fault+0x54/0x510
Aug 19 08:32:38 mybank-ant kernel: : [116]
[&ffffffff&] ? __generic_file_aio_write+0x240/0x470
Aug 19 08:32:38 mybank-ant kernel: : [118]
[&ffffffff&] ? handle_pte_fault+0xf7/0xb50
Aug 19 08:32:38 mybank-ant kernel: : [121]
[&ffffffff8111438e&] ? generic_file_aio_write+0xbe/0xe0
Aug 19 08:32:38 mybank-ant kernel: : [133]
[&ffffffffa008a171&] ? ext4_file_write+0x61/0x1e0 [ext4]
Aug 19 08:32:38 mybank-ant kernel: : [135]
[&ffffffff8113dc54&] ? handle_mm_fault+0x1e4/0x2b0
Aug 19 08:32:38 mybank-ant kernel: : [138]
[&ffffffff81177c7a&] ? do_sync_write+0xfa/0x140
Aug 19 08:32:38 mybank-ant kernel: : [143]
[&ffffffff81042c69&] ? __do_page_fault+0x139/0x480
Aug 19 08:32:38 mybank-ant kernel: : [147]
[&ffffffff8118ad22&] ? vfs_ioctl+0x22/0xa0
Aug 19 08:32:38 mybank-ant kernel: : [151]
[&ffffffff814e4f8e&] ? do_page_fault+0x3e/0xa0
Aug 19 08:32:38 mybank-ant kernel: : [154]
[&ffffffff814e2345&] ? page_fault+0x25/0x30
Aug 19 08:32:38 mybank-ant kernel: : [969] [24673]
Aug 19 08:32:38 mybank-ant kernel: : [971] [25084]
Aug 19 08:32:38 mybank-ant kernel: : [973] [25094]
Aug 19 08:32:38 mybank-ant kernel: : [975] [25098]
Aug 19 08:32:38 mybank-ant kernel: : [977] [25100]
Aug 19 08:32:38 mybank-ant kernel: : [979] [25485]
Aug 19 08:32:38 mybank-ant kernel: : [981] [26055]
Aug 19 08:32:38 mybank-ant kernel: : [984] [26069]
Aug 19 08:32:38 mybank-ant kernel: : [986] [26081]
Aug 19 08:32:38 mybank-ant kernel: : [988] [26147]
Aug 19 08:32:38 mybank-ant kernel: : [990] Out of memory: Kill process 24673 (java) score 946 or sacrifice child
Aug 19 08:32:38 mybank-ant kernel: : [016] Killed process 24673, UID 1801, (java) total-vm:5120504kB, anon-rss:3703788kB, file-rss:484kB
从上面我们看到了一个堆栈,也就是内核里选择被kill进程的过程,这个过程会对进程进行一系列的计算,每个进程都会给它们计算一个score,这个分数会记录在/proc/&pid&/oom_score里,通常这个分数越高,就越危险,被kill的可能性就越大,下面将内核相关的代码贴出来,有兴趣的可以看看,其中代码注释上也写了挺多相关的东西了:
* Simple selection loop. We chose the process with the highest
* number of 'points'. We expect the caller will lock the tasklist.
* (not docbooked, we don't want this one cluttering up the manual)
static struct task_struct *select_bad_process(unsigned long *ppoints,
struct mem_cgroup *mem)
struct task_struct *p;
struct task_struct *chosen = NULL;
*ppoints = 0;
do_posix_clock_monotonic_gettime(&uptime);
for_each_process(p) {
* skip kernel threads and tasks which have already released
* their mm.
if (!p-&mm)
/* skip the init task */
if (is_global_init(p))
if (mem && !task_in_mem_cgroup(p, mem))
* This task already has access to memory reserves and is
* being killed. Don't allow any other task access to the
* memory reserve.
* Note: this may have a chance of deadlock if it gets
* blocked waiting for another task which itself is waiting
* for memory. Is there a better alternative?
if (test_tsk_thread_flag(p, TIF_MEMDIE))
return ERR_PTR(-1UL);
* This is in the process of releasing memory so wait for it
* to finish before killing some other task by mistake.
* However, if p is the current task, we allow the 'kill' to
* go ahead if it is exiting: this will simply set TIF_MEMDIE,
* which will allow it to gain access to memory reserves in
* the process of exiting and releasing its resources.
* Otherwise we could get an easy OOM deadlock.
if (p-&flags & PF_EXITING) {
if (p != current)
return ERR_PTR(-1UL);
*ppoints = ULONG_MAX;
if (p-&signal-&oom_adj == OOM_DISABLE)
points = badness(p, uptime.tv_sec);
if (points & *ppoints || !chosen) {
*ppoints =
* badness - calculate a numeric value for how bad this task has been
* @p: task struct of which task we should calculate
* @uptime: current uptime in seconds
* The formula used is relatively simple and documented inline in the
* function. The main rationale is that we want to select a good task
* to kill when we run out of memory.
* Good in this context means that:
* 1) we lose the minimum amount of work done
* 2) we recover a large amount of memory
* 3) we don't kill anything innocent of eating tons of memory
* 4) we want to kill the minimum amount of processes (one)
* 5) we try to kill the process the user expects us to kill, this
algorithm has been meticulously tuned to meet the principle
of least surprise ... (be careful when you change it)
unsigned long badness(struct task_struct *p, unsigned long uptime)
unsigned long points, cpu_time, run_
struct mm_struct *
struct task_struct *
int oom_adj = p-&signal-&oom_
struct task_cputime task_
if (oom_adj == OOM_DISABLE)
task_lock(p);
if (!mm) {
task_unlock(p);
* The memory size of the process is the basis for the badness.
points = mm-&total_
* After this unlock we can no longer dereference local variable `mm'
task_unlock(p);
* swapoff can easily use up all memory, so kill those first.
if (p-&flags & PF_OOM_ORIGIN)
return ULONG_MAX;
* Processes which fork a lot of child processes are likely
* a good choice. We add half the vmsize of the children if they
* have an own mm. This prevents forking servers to flood the
* machine with an endless amount of children. In case a single
* child is eating the vast majority of memory, adding only half
* to the parents will make the child our kill candidate of choice.
list_for_each_entry(child, &p-&children, sibling) {
task_lock(child);
if (child-&mm != mm && child-&mm)
points += child-&mm-&total_vm/2 + 1;
task_unlock(child);
* CPU time is in tens of seconds and run time is in thousands
* of seconds. There is no particular reason for this other than
* that it turned out to work very well in practice.
thread_group_cputime(p, &task_time);
utime = cputime_to_jiffies(task_time.utime);
stime = cputime_to_jiffies(task_time.stime);
cpu_time = (utime + stime) && (SHIFT_HZ + 3);
if (uptime &= p-&start_time.tv_sec)
run_time = (uptime - p-&start_time.tv_sec) && 10;
run_time = 0;
if (cpu_time)
points /= int_sqrt(cpu_time);
if (run_time)
points /= int_sqrt(int_sqrt(run_time));
* Niced processes are most likely less important, so double
* their badness points.
if (task_nice(p) & 0)
points *= 2;
* Superuser processes are usually more important, so we make it
* less likely that we kill those.
if (has_capability_noaudit(p, CAP_SYS_ADMIN) ||
has_capability_noaudit(p, CAP_SYS_RESOURCE))
points /= 4;
* We don't want to kill a process with direct hardware access.
* Not only could that mess up the hardware, but usually users
* tend to only have this flag set on applications they think
* of as important.
if (has_capability_noaudit(p, CAP_SYS_RAWIO))
points /= 4;
* If p's nodes don't overlap ours, it may still help to kill p
* because p may have allocated or otherwise mapped memory on
* this node before. However it will be less likely.
if (!has_intersects_mems_allowed(p))
points /= 8;
* Adjust the score by oom_adj.
if (oom_adj) {
if (oom_adj & 0) {
if (!points)
points = 1;
points &&= oom_
points &&= -(oom_adj);
#ifdef DEBUG
printk(KERN_DEBUG "OOMkill: task %d (%s) got %lu points\n",
p-&pid, p-&comm, points);
物理内存到底去哪了?
DirectByteBuffer冰山对象?
这是我们查这个问题首先要想到的一个地方,是否是因为什么地方不断创建DirectByteBuffer对象,但是由于没有被回收导致了内存泄露呢,之前有篇文章已经详细介绍了这种特殊对象JVM源码分析之堆外内存完全解读,对阿里内部的童鞋,可以直接使用zprofiler的heap视图里的堆外内存分析功能拿到统计结果,知道后台到底绑定了多少堆外内存还没有被回收:
java.nio.DirectByteBuffer @ 0x760afaed0
133 133 6380562
java.nio.DirectByteBuffer @ 0x790d51ae0
java.nio.DirectByteBuffer @ 0x790d20b80
java.nio.DirectByteBuffer @ 0x790d20b40
java.nio.DirectByteBuffer @ 0x790d20b00
java.nio.DirectByteBuffer @ 0x771ba3608
java.nio.DirectByteBuffer @ 0x771ba35c8
java.nio.DirectByteBuffer @ 0x7c5c9e250
java.nio.DirectByteBuffer @ 0x7c5c9e210
java.nio.DirectByteBuffer @ 0x7c185cd10
java.nio.DirectByteBuffer @ 0x7c185ccd0
java.nio.DirectByteBuffer @ 0x7b181c980
java.nio.DirectByteBuffer @ 0x7a40d6e40
java.nio.DirectByteBuffer @ 0x794ac3320
java.nio.DirectByteBuffer @ 0x794a7a418
java.nio.DirectByteBuffer @ 0x
java.nio.DirectByteBuffer @ 0x77279dde8
java.nio.DirectByteBuffer @ 0x76ea84000
java.nio.DirectByteBuffer @ 0x76ea83fc0
java.nio.DirectByteBuffer @ 0x764d8d678
java.nio.DirectByteBuffer @ 0x764d8d638
java.nio.DirectByteBuffer @ 0x764d8d5f8
java.nio.DirectByteBuffer @ 0x761a76340
java.nio.DirectByteBuffer @ 0x761a7
java.nio.DirectByteBuffer @ 0x
总共: 25 / 875 条目; 还有850条,双击展开
某个动态库里频繁分配?
对于动态库里频繁分配的问题,主要得使用google的perftools工具了,该工具网上介绍挺多的,就不对其用法做详细介绍了,通过该工具我们能得到native方法分配内存的情况,该工具主要利用了unix的一个环境变量LD_PRELOAD,它允许你要加载的动态库优先加载起来,相当于一个Hook了,于是可以针对同一个函数可以选择不同的动态库里的实现了,比如googleperftools就是将malloc方法替换成了tcmalloc的实现,这样就可以跟踪内存分配路径了,得到的效果类似如下:
Total: 1670.0 MB
.8% zcalloc
2.4% os::malloc
0.1% readCEN
0.1% ObjectSynchronizer::omAlloc
0.0% 100.0%
.3% Java_java_util_zip_Deflater_init
0.0% 100.0%
0.0% _dl_allocate_tls
0.0% 100.0%
0.0% addMetaName
0.0% 100.0%
0.0% allocZip
0.0% 100.0%
0.0% instanceKlass::add_dependent_nmethod
0.0% 100.0%
0.0% newEntry
0.0% 100.0%
0.0% strdup
0.0% 100.0%
1.5% Java_java_util_zip_Inflater_init
0.0% 100.0%
0.0% growMetaNames
0.0% 100.0%
0.0% _dl_new_object
0.0% 100.0%
0.0% _2.2.5
0.0% 100.0%
0.1% Thread::Thread
0.0% 100.0%
0.0% _2.2.5
0.0% 100.0%
0.0% JLI_MemAlloc
0.0% 100.0%
0.0% read_alias_file
0.0% 100.0%
0.0% _nl_intern_locale_data
0.0% 100.0%
0.0% nss_parse_service_list
0.0% 100.0%
0.0% getprotobyname
0.0% 100.0%
0.0% getpwuid
0.0% 100.0%
0.0% _dl_check_map_versions
0.0% 100.0%
.2% deflateInit2_
从上面的输出中我们看到了zcalloc函数总共分配了1616.3M的内存,还有Java_java_util_zip_Deflater_init分配了1591.0M内存,deflateInit2_分配了1590.5M,然而总共才分配了1670.0M内存,所以这几个函数肯定是调用者和被调用者的关系:
JNIEXPORT jlong JNICALL
Java_java_util_zip_Deflater_init(JNIEnv *env, jclass cls, jint level,
jint strategy, jboolean nowrap)
z_stream *strm = calloc(1, sizeof(z_stream));
if (strm == 0) {
JNU_ThrowOutOfMemoryError(env, 0);
return jlong_
switch (deflateInit2(strm, level, Z_DEFLATED,
nowrap ? -MAX_WBITS : MAX_WBITS,
DEF_MEM_LEVEL, strategy)) {
case Z_OK:
return ptr_to_jlong(strm);
case Z_MEM_ERROR:
free(strm);
JNU_ThrowOutOfMemoryError(env, 0);
return jlong_
case Z_STREAM_ERROR:
free(strm);
JNU_ThrowIllegalArgumentException(env, 0);
return jlong_
msg = strm-&
free(strm);
JNU_ThrowInternalError(env, msg);
return jlong_
int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
version, stream_size)
const char *
int stream_
deflate_state *s;
int wrap = 1;
static const char my_version[] = ZLIB_VERSION;
/* We overlay pending_buf and d_buf+l_buf. This works since the average
* output size for (length,distance) codes is &= 24 bits.
if (version == Z_NULL || version[0] != my_version[0] ||
stream_size != sizeof(z_stream)) {
return Z_VERSION_ERROR;
if (strm == Z_NULL) return Z_STREAM_ERROR;
strm-&msg = Z_NULL;
if (strm-&zalloc == (alloc_func)0) {
strm-&zalloc =
strm-&opaque = (voidpf)0;
if (strm-&zfree == (free_func)0) strm-&zfree =
#ifdef FASTEST
if (level != 0) level = 1;
if (level == Z_DEFAULT_COMPRESSION) level = 6;
if (windowBits & 0) { /* suppress zlib wrapper */
windowBits = -windowB
#ifdef GZIP
else if (windowBits & 15) {
/* write gzip wrapper instead */
windowBits -= 16;
if (memLevel & 1 || memLevel & MAX_MEM_LEVEL || method != Z_DEFLATED ||
windowBits & 8 || windowBits & 15 || level & 0 || level & 9 ||
strategy & 0 || strategy & Z_FIXED) {
return Z_STREAM_ERROR;
if (windowBits == 8) windowBits = 9;
/* until 256-byte window bug fixed */
s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
if (s == Z_NULL) return Z_MEM_ERROR;
strm-&state = (struct internal_state FAR *)s;
s-&gzhead = Z_NULL;
s-&w_bits = windowB
s-&w_size = 1 && s-&w_
s-&w_mask = s-&w_size - 1;
s-&hash_bits = memLevel + 7;
s-&hash_size = 1 && s-&hash_
s-&hash_mask = s-&hash_size - 1;
s-&hash_shift =
((s-&hash_bits+MIN_MATCH-1)/MIN_MATCH);
s-&window = (Bytef *) ZALLOC(strm, s-&w_size, 2*sizeof(Byte));
= (Posf *)
ZALLOC(strm, s-&w_size, sizeof(Pos));
= (Posf *)
ZALLOC(strm, s-&hash_size, sizeof(Pos));
s-&lit_bufsize = 1 && (memLevel + 6); /* 16K elements by default */
overlay = (ushf *) ZALLOC(strm, s-&lit_bufsize, sizeof(ush)+2);
s-&pending_buf = (uchf *)
s-&pending_buf_size = (ulg)s-&lit_bufsize * (sizeof(ush)+2L);
if (s-&window == Z_NULL || s-&prev == Z_NULL || s-&head == Z_NULL ||
s-&pending_buf == Z_NULL) {
s-&status = FINISH_STATE;
strm-&msg = (char*)ERR_MSG(Z_MEM_ERROR);
deflateEnd (strm);
return Z_MEM_ERROR;
s-&d_buf = overlay + s-&lit_bufsize/sizeof(ush);
s-&l_buf = s-&pending_buf + (1+sizeof(ush))*s-&lit_
s-&level =
s-&strategy =
s-&method = (Byte)
return deflateReset(strm);
上述代码也验证了他们这种关系。
那现在的问题就是找出哪里调用Java_java_util_zip_Deflater_init了,从这方法的命名上知道它是一个java的native方法实现,对应的是java.util.zip.Deflater这个类的init方法,所以要知道init方法哪里被调用了,跟踪调用栈我们会想到btrace工具,但是btrace是通过插桩的方式来实现的,对于native方法是无法插桩的,于是我们看调用它的地方,找到对应的方法,然后进行btrace脚本编写:
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace public class Test {
@OnMethod(
clazz="java.util.zip.Deflater",
method="&init&"
public static void onnewThread(int i,boolean b) {
于是跟踪对应的进程,我们能抓到调用Deflater构造函数的堆栈
pressors.deflate.DeflateCompressorOutputStream.&init&(DeflateCompressorOutputStream.java:47)
com.xxx.unimsg.pressUtil.deflateCompressAndEncode(CompressUtil.java:199)
com.xxx.unimsg.press(CompressUtil.java:80)
com.xxx.pressXml(UnifyMessageHelper.java:65)
com.xxx.core.model.pressXml(UnifyMessageUtil.java:56)
com.xxx.repository.convert.BatchInDetailConvert.convertDO(BatchInDetailConvert.java:57)
com.xxx.repository.impl.IncomingDetailRepositoryImpl$1.store(IncomingDetailRepositoryImpl.java:43)
com.xxx.repository.helper.IdempotenceHelper.store(IdempotenceHelper.java:27)
com.xxx.repository.impl.IncomingDetailRepositoryImpl.store(IncomingDetailRepositoryImpl.java:40)
sun.reflect.GeneratedMethodAccessor274.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
java.lang.reflect.Method.invoke(Method.java:597)
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
com.ponent.monitor.MethodMonitorInterceptor.invoke(MethodMonitorInterceptor.java:45)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
从上面的堆栈我们找出了调用java.util.zip.Deflate.init()的地方
上面已经定位了具体的代码了,于是再细致跟踪了下对应的代码,其实并不是代码实现上的问题,而是代码设计上没有考虑到流量很大的场景,当流量很大的时候,不管自己系统是否能承受这么大的压力,都来者不拒,拿到数据就做deflate,而这个过程是需要分配堆外内存的,当量达到一定程度的时候此时会发生oom killer,另外我们在分析过程中发现其实物理内存是有下降的
30071.txt:
0.0% 100.0%
57.0% Java_java_util_zip_Deflater_init
30071.txt:
72.6% Java_java_util_zip_Deflater_init
30071.txt:
78.5% Java_java_util_zip_Deflater_init
30071.txt:
83.6% Java_java_util_zip_Deflater_init
30071.txt:
88.5% Java_java_util_zip_Deflater_init
30071.txt:
91.0% Java_java_util_zip_Deflater_init
30071.txt:
91.9% Java_java_util_zip_Deflater_init
30071.txt:
92.2% Java_java_util_zip_Deflater_init
30071.txt:
63.7% Java_java_util_zip_Deflater_init
30071.txt:
0.0% 100.0%
52.1% Java_java_util_zip_Deflater_init
30071.txt:
87.4% Java_java_util_zip_Deflater_init
30071.txt:
90.1% Java_java_util_zip_Deflater_init
30071.txt:
92.3% Java_java_util_zip_Deflater_init
30071.txt:
.9% Java_java_util_zip_Deflater_init
30071.txt:
.3% Java_java_util_zip_Deflater_init
30071.txt:
92.1% Java_java_util_zip_Deflater_init
30071.txt:
0.0% 100.0%
.1% Java_java_util_zip_Deflater_init
30071.txt:
0.0% 100.0%
.9% Java_java_util_zip_Deflater_init
30071.txt:
0.0% 100.0%
.1% Java_java_util_zip_Deflater_init
30071.txt:
0.0% 100.0%
.3% Java_java_util_zip_Deflater_init
30071.txt:
90.0% Java_java_util_zip_Deflater_init
30071.txt:
92.8% Java_java_util_zip_Deflater_init
30071.txt:
92.3% Java_java_util_zip_Deflater_init
30071.txt:
91.9% Java_java_util_zip_Deflater_init
30071.txt:
91.2% Java_java_util_zip_Deflater_init
30071.txt:
77.9% Java_java_util_zip_Deflater_init
30071.txt:
.1% Java_java_util_zip_Deflater_init
30071.txt:
90.6% Java_java_util_zip_Deflater_init
这也就说明了其实代码使用上并没有错,因此建议将deflate放到队列里去做,比如限制队列大小是100,每次最多100个数据可以被deflate,处理一个放进一个,以至于不会被活活撑死。
12345678910
12345678910
12345678910 上一篇:下一篇:文章评论相关解决方案 1234567891011 Copyright & &&版权所有

参考资料

 

随机推荐