案例分析:java中substring引发的Full gc
问题定位
由于应用频繁地Full gc,就dump了内存下来用MAT分析,发现有个map占用了98%的内存,于是找到这个map
private ConcurrentMap<String, String> nick2numid = new ConcurrentHashMap<String, String>();
存放的是nick与id的映射关系,从MAT中找到map的每一个entry如下图所示:
这里解释一下两个概念
Shallow Heap:对象占用了多少内存(单位:字节)
Retained Heap:如果对象被回收会释放多少内存,也就是对象hold住的内存
map的key是一个String类型,其Shallow Heap为32byte,Retained Heap为1104byte。一般对于String类型,具有不可变性,这两个值应该相等才对,带着疑惑找到了问题所在,分步描述如下:
1、从页面传了一个参数到后端,这个参数携带了cookie的内容,恰好就是1104byte这么长,不妨设置这个参数为cookie;
2、后端拿到cookie这个参数后,需要其中的nick的值,采用的是String类的split方法(&做为分隔符)得到一个数组,其中有一项的值为nick=xxx
3、将nick的值做为key放入nick2numid中
nick2numid.put("xxx",id)
最终发现问题出在String类的substring方法上?
分析问题
其实String的split方法上调用了substring方法,先来看看split的源码实现吧
public String[] split(String regex) {
return split(regex, 0);
}
public String[] split(String regex, int limit) {
return Pattern.compile(regex).split(this, limit);
}
public String[] split(CharSequence input, int limit) {
ArrayList<String> matchList = new ArrayList<String>();
Matcher m = matcher(input);
// Add segments before each match found
while(m.find()) {
if (!matchLimited || matchList.size() < limit - 1) {
String match = input.subSequence(index, m.start()).toString();
matchList.add(match);
index = m.end();
} else if (matchList.size() == limit - 1) { // last one
String match = input.subSequence(index,
input.length()).toString();
matchList.add(match);
index = m.end();
}
}
}
public CharSequence subSequence(int beginIndex, int endIndex) {
return this.substring(beginIndex, endIndex);
}
String.split->Pattern.split->subSequence->substring
从以上代码可以看出String类的split方法确实调用了substring方法
下面来看看substring方法源码:
private final char value[];
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > count) {
throw new StringIndexOutOfBoundsException(endIndex);
}
if (beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
}
return ((beginIndex == 0) && (endIndex == count)) ? this :
new String(offset + beginIndex, endIndex - beginIndex, value);
}
注意value这个char数组,存放的是String每个字符的内容,substring直接依赖了这个数组。如果substring产生的字符串没有被java虚拟机回收,这个char数组也不会被回收。
问题回顾:应用中的nick2numId这个map生命周期很长,只有在用户退出的时候才会删除其中的entry项,恰好这个map非常大,dump内存的时候size已经达到几十万,相当于几十万个cookie被这个map所hold住,内存被耗尽产生Full gc。
解决方案
采用第一种方法是否可行?不可取,原因有2:
intern() 所使用的是一个全局的池,并不需要如此大作用域的缓存;
intern会向常量池中添加内容,持久代空间本来就很小,被nick所占用可能引起OutOfMemoryError!
后来采用的是第二种方法:new String(nick.toCharArray()),很快上线使内存利用率得到提升(fast,not the best)。
JVM中存放生命周期长的大map始终是一个隐患,说不定哪一天由于map元素过多或者删除不及时导致OOM,应尽快采用第3种方案:分布式缓存。nick放入缓存之后,cookie会很快被回收,而且系统的可用内存将会大大提高。
案例分析:java中substring引发的Full gc的更多相关文章
- java中substring和indexof() 和lastindexof()
java中substring和indexof() 和lastindexof() str=str.substring(int beginIndex);截取掉str从首字母起长度为beginIndex的字 ...
- java中substring的使用方法
java中substring的使用方法 str=str.substring(int beginIndex);截取掉str从首字母起长度为beginIndex的字符串,将剩余字符串赋值给str: str ...
- 从虚拟机指令执行的角度分析JAVA中多态的实现原理
从虚拟机指令执行的角度分析JAVA中多态的实现原理 前几天突然被一个"家伙"问了几个问题,其中一个是:JAVA中的多态的实现原理是什么? 我一想,这肯定不是从语法的角度来阐释多态吧 ...
- Oracle trunc()函数,decode()函数,substr函数,GREATEST函数,java中substring函数的用法
--Oracle trunc()函数的用法/**************日期********************/1.select trunc(sysdate) from dual --2013- ...
- 分析Java中的length和length()
在不适用任何带有自动补全功能的IDE的情况下,我们怎么获取一个数组的长度?如何获取字符串的长度? 这里我们先举用实例去分析一下:int[] arr=new int[3]:System.out.prin ...
- 详细分析 Java 中实现多线程的方法有几种?(从本质上出发)
详细分析 Java 中实现多线程的方法有几种?(从本质上出发) 正确的说法(从本质上出发) 实现多线程的官方正确方法: 2 种. Oracle 官网的文档说明 方法小结 方法一: 实现 Runnabl ...
- 详细分析 Java 中启动线程的正确和错误方式
目录 启动线程的正确和错误方式 前文回顾 start 方法和 run 方法的比较 start 方法分析 start 方法的含义以及注意事项 start 方法源码分析 源码 源码中的流程 run 方法分 ...
- Java中substring函数的简单应用
1.删掉一个字符串中的某个字符 /* * 使用Java 中的 substring()函数删掉字符串中的某个字符 * deleteAssignChar函数的参数说明: * str:被操作的字符串 * o ...
- 通过String的不变性案例分析Java变量的可变性
阅读本文之前,请先看以下几个问题: 1.String变量是什么不变?final修饰变量时的不变性指的又是什么不变,是引用?还是内存地址?还是值? 2.java对象进行重赋值或者改变属性时在内存中是如何 ...
随机推荐
- jvm监控命令介绍
1.jstack介绍 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在 ...
- HDOJ 1166.敌兵布阵
2015-06-08 问题简述: 原题的题意相当于有一些连续摆放的箱子,里面装着球,球的数量可以加减,现要查询几个连续的箱子里球的总数,其中存在放球和拿球的操作. 原题链接:http://acm.hd ...
- BestCoder Round #38
1001 Four Inages Strategy 题意:给定空间的四个点,判断这四个点是否能形成正方形 思路:判断空间上4个点是否形成一个正方形方法有很多,这里给出一种方法,在p2,p3,p4中枚举 ...
- js面向对象的三大特性
0x00:使用OOP技术,常常要使用许多的代码模块,每个模块都提供特定的功能,每个模块老师孤立的,甚至与其它的模块完全独立,这种模块化的编程方法大大的提供了代码实现的多样性,大大增加了代码的重用性.j ...
- 玩转无线 — GNURADIO 简单运用
大家好, 我是Insight-labs的旺财,这里放出个旺财在Bsides Toronto 2013 会上RF-Ninjia Hacking议题中的一个案例,随着物联网越来越火热,而物联网又离不开无线 ...
- utf8_general_ci 、utf8_general_cs和utf8_bin的区别
用了这么长时间,发现自己竟然不知道utf_bin和utf_general_ci这两者到底有什么区别..ci是 case insensitive, 即 "大小写不敏感", a 和 A ...
- [转载]各种在线api地址
J2SE1.7英文api地址: http://download.oracle.com/javase/7/docs/api/J2SE1.6英文api地址: http://download.oracle ...
- SqlServer2012导入Oracle详细案例
第一次使用SqlServer2012,界面和VS2012风格一致了,Great! 进入正题,这篇博文主要写一下自己亲测的一个案例,使用SqlServer2012的“导入和导出数据”功能向Oracle1 ...
- HTTP学习笔记--HTTP报文
报文流 HTTP报文在客户端.服务器和代理之间流动.“流入”.“流出”.“上游”.“下游”这些术语用来描述报文方向. 报文流入源端服务器 流入:流向服务器 流出:流向用户Age ...
- sign a third-party dll which don't have a strong name
Problem: Assembly generation failed -- Referenced assembly '' does not have a strong name Cause: thi ...