产品中心PRDUCTS DISPLAY

联系我们

联系人:张生

咨询热线:400-123-4657

传真:+86-123-4567

手机:13800000000

邮箱:admin@youweb.com

地址:广东省广州市天河区88号

在线咨询

新闻动态

您现在的位置是: 首页 > 新闻动态

生产环境出现内存溢出问题了!

!!你能处理这个问题吗?大家好,我是冰河。最近,有一位小伙伴跟我反映,他的程序在测试环境中没有任何问题,但是在生产环境中却经常出现内存溢出的情况,这个问题已经困扰他一周多了。因此,在周末的时候,我开始帮助他找出各种问题。小伙伴提出的问题确实需要一番时间来排查。我直接说说我们定位到的问题吧。接下来,我会撰写一篇详尽的文章,讲述问题排查的具体过程。在排查问题时,我发现这位同事正在使用JDK 1.6版本。一开始,我并没有多想,继续检查他所编写的代码,也没有发现什么错误。然而一开启生产环境的程序不久后,JVM 就会抛出内存溢出的异常。这真是奇怪,发生了什么事情呢?无论在启动程序时加上多合理的JVM参数,问题仍然存在。。。唉,没办法,只能继续研究他的代码了!偶然间,我注意到他的代码中大量使用了String类的substring()方法来对字符串进行截取。因此,我开始跟踪JDK中的代码,以查看传入的参数。这次偶然的浏览竟然让我找到了问题的根源!!经过分析发现,在JDK1.6中String类竟然存在一个巨大的问题!为什么说它是个陷阱呢?因为它的substring()方法会让人很糟糕!不需多言,让我们先来了解一下JDK1.6中的String类的substring()方法。

公开的 字符串 子串(int 起始下标,如果(beginIndex小于0) {\n throw new StringIndexOutOfBoundsException(beginIndex);\ \n如果(endIndex大于count) {\n throw new StringIndexOutOfBoundsException(endIndex);\ \n如果(beginIndex大于endIndex) {\n throw new StringIndexOutOfBoundsException(endIndex - beginIndex);\ \nreturn ((beginIndex == 0) && (endIndex == count)) ? 这是 JDK1.6 中 String类的 一个构造方法: 

 new String(offset + beginIndex, endIndex - beginIndex, value); 。 请注意,它可以将字符串的一部分复制到一个新的字符串对象中。

String(int offset, int count, char[] value){     this.value = value;     this.offset = offset;     this.count = count; } 

观察上述代码,细心的读者可能已经察觉到这里的问题所在,导致问题产生的root cause正是在下方这一行代码。在JDK1.6中,使用String类的构造函数创建子字符串时,并不仅仅是简单地复制所需的对象,而是每次都会将整个value引用进来。在JDK1.6版本中,使用String类的构造函数创建子字符串时,并不仅仅是简单地复制所需的对象,而是每次都会将整个value引用进来。如果原始字符串比较大,即使该字符串不再被使用,其所分配的内存也不会被释放。这是我对代码进行长时间分析后得出的结论,确实相当糟糕!!问题既然找到了,那我们就要解决它。升级JDK至 版本是解决在JDK1.6中String类存在的严重问题的最直接有效的方法。我便和小伙伴解释了情况,希望他可以将JDK升级至JDK1.8版本。

我们也来看一下JDK1.8中String类的substring()方法。在公共类中,字符串子串(int beginIndex,截取字符串的部分可以写成这样:if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } int subLen = endIndex - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ?在JDK1.8版本中的String类的substring()函数中,也会使用String类的构造函数来创建子字符串,让我们来看看这个构造函数,如下所示。{X}{N}{X}公开的{P}字符串(char{P}值[],{P}int{P}偏移量,打印计数{{if(offset<0){抛出新的StringIndexOutOfBoundsException(offset);}{\n如果(count<=0){if(count<0){抛出新的StringIndexOutOfBoundsException(count);}}if(offset<=value.length){这个.value = "".value;返回;}}}//注意:偏移量或计数可能接近-1>>>1。if(offset>value.length-count){抛出新的StringIndexOutOfBoundsException(offset+count);}}this.value = Arrays.copyOfRange(value;在JDK1.8中,当我们需要获取一个字符串的子串时,使用substring函数会生成一个新的字符串。这个新字符串是通过调用Arrays.copyOfRange构造函数来得到的,该函数会复制原始字符串的指定范围的字符。例如,substring(offset,offset+count)就会生成原始字符串的第offset个字符到第offset+count-1个字符的子字符串。这个没有问题。没问题。

我对JVM启动参数进行了优化,以更好地提升系统的性能。我帮助了这位小伙伴优化了JVM启动参数。根据小伙伴授权,我简要列出他们的业务规模和服务器配置:系统采用分布式架构,各业务服务采用集群部署,日均访问量达到上亿,日均交易订单在50万至100万之间,订单系统的服务器节点配置为4核8G。已经将 JDK 升级至 1.8 版本。根据以上条件,我提供了JVM调优后的参数设置。针对上述JVM参数配置,我会在后续的文章中详细分析如何根据实际业务场景进行JVM参数调优。如果在程序中创建了一个较大的对象,并且在此基础上生成了其他信息,那么一定要释放与这个大对象的引用关系,否则就可能引发内存溢出的问题。JVM 优化的目标是尽量让对象在新生代进行分配和回收,减少对象频繁进入老年代,避免频繁老年代的垃圾回收,确保系统有足够的内存大小,减少新生代的垃圾回收频率。

 

在线客服

关注我们 在线咨询 投诉建议 返回顶部