当前位置:首页 > CN2资讯 > 正文内容

我的世界java内存参数

2天前CN2资讯

  注:本文主要记录这次解决内存溢出问题的过程而不是具体问题。

  最近在写一个搜索引擎,使用倒排索引结构进行文档检索,保存索引的基本思想是先将倒排列表保存到内存中一个有序Map里(TreeMap),然后当内存占用达到一定阈值的时候将内存中的倒排列表有序写入磁盘,当磁盘已经存在索引时,则将内存中的索引和磁盘中的索引进行合并,生成新的索引,合并过程类似于归并排序。合并内存索引和磁盘索引的代码如下:

public synchronized void merge(){ ("InvertIndex merge start..."); File f=new File(path); //这个文件是原磁盘上的索引文件 //磁盘存在索引,合并磁盘索引和内存索引 if (f.exists()) { String outPath=path+".temp"; File outFile=new File(outPath); TreeMap<String, TreeSet<Long>> ramSnapshot=null; ramSnapshot=ram; //ram保存的是内存索引,这里因为ram可能被其他添加文档的线程修改,因此先存一份快照然后将ram清空,后面实际操作的是快照 ram=new TreeMap<>(); BufferedReader reader=null; PrintWriter writer=null; try {           //合并过程,有3个指针:分别指向内存索引的当前位置、原磁盘索引读到的位置、新的磁盘索引写入的位置 Iterator<Entry<String, TreeSet<Long>>> ramIterator= ramSnapshot.entrySet().iterator(); reader=new BufferedReader(new FileReader(f)); writer=new PrintWriter(new BufferedWriter(new FileWriter(outFile))); Entry<String, TreeSet<Long>> entry=ramIterator.hasNext()?ramIterator.next():null; String line=reader.readLine(); while (entry!=null&&line!=null) { long freeRam=Runtime.getRuntime().freeMemory()/1000/1000; System.out.println("freeRam: "+freeRam); String ramWord=entry.getKey(); String diskWord=line.split(separator1)[0]; String out=""; int c=ramWord.compareTo(diskWord);             //合并过程,因为是两个有序列表,采用类似归并排序的合并方法,区别在于这里如果遇到倒排词相等的时候,需要合并到一个倒排词(合并两者文档列表) if (c==0) { TreeSet<Long> ramDocIds=entry.getValue(); TreeSet<Long> diskDocIds=this.convertLine2DocIds(line); TreeSet<Long> union=ramDocIds; union.addAll(diskDocIds); out=this.convertIndex2Line(ramWord, union); entry=ramIterator.hasNext()?ramIterator.next():null; line=reader.readLine(); }else if (c<0) { out=this.convertIndex2Line(ramWord, entry.getValue()); entry=ramIterator.hasNext()?ramIterator.next():null; }else { out=this.convertIndex2Line(diskWord, this.convertLine2DocIds(line)); line=reader.readLine(); } writer.println(out); } ("InvertIndex complex merge complete."); while (ramIterator.hasNext()) { entry=ramIterator.next(); String out=this.convertIndex2Line(entry.getKey(), entry.getValue()); writer.println(out); } ("InvertIndex ram merge complete."); while ((line=reader.readLine())!=null) { writer.println(line); } ("InvertIndex disk merge complete."); } catch (Exception e) { LogUtil.err("merge ram index and disk index fail.", e); }finally { try { if (reader!=null) { reader.close(); } if (writer!=null) { writer.close(); } } catch (Exception e2) {LogUtil.err("release resource fail.", e2);} } f.delete(); if (!outFile.renameTo(new File(path))) { throw new RuntimeException("rename temp file fail."); } }else { //磁盘上原本不存在索引,直接将内存索引写入磁盘 //代码略 }

  代码的主要思想是维持3个指针:内存索引是一个有序TreeMap,iterator相当于一个虚拟指针;磁盘索引也是有序的,BufferdReader相当于一个虚拟指针;合并后生成的新索引使用BufferdWriter作为指针。然后使用归并排序的思想,比较内存索引词和磁盘索引词的大小,哪个小就将哪个写入新的索引然后将其指针前进一位,如果两个索引词相等,则合并两者的文档列表。

  从上面的描述来看以上代码使用的内存应该是O(1)的,因为内存中除了内存索引之外,同一时刻只会从磁盘索引读出一行,但是实际运行的时候,总是在合并时报出GC overhead limit exceeded,这个异常就是说jvm用了大量时间(超过98%)执行GC但是只释放了很少的堆内存(小于2%),换句话说就是OOM的前兆。根据我对程序的内存占用的分析,这种情况是不正常的。

  于是给程序添加  “-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:/dump” 参数,在程序出现内存溢出异常的时候dump出内存信息。

  用eclipse MAT查看占用内存最多的对象:

发现是TreeMap的实例,在这个程序中我用了TreeMap来保存内存索引,而在合并索引的过程中,内存索引的大小应该是不变的,那么为什么会溢出呢?

  通过看代码将问题定位在了红色代码处:

这里首先将ramDocIds指向entry.getValue(),然后又将union指向ramDocIds,此时union实际上是直接指向entry.getValue()的,然后union执行了addAll操作。。众所周知,addAll操作是将元素加到对象本身的,这里我的原意是声明一个局部变量保存列表合并结果然后存入新的索引文件,但是无意中却同时修改了内存索引导致内存索引越来越大。

 

OK,到这里问题就解决了,只需要将

TreeSet<Long> union=ramDocIds;

改为

TreeSet<Long> union=new TreeSet<>(ramDocIds); 

即可。

 

回顾一下这次解决内存溢出问题的过程:  

  • 分析程序空间复杂度,看内存溢出的是否正常。
  • 添加jvm参数,让程序在内存溢出的时候dump出内存快照。
  • 使用eclipse MAT分析占用内存最多的对象。
  • 在源码中找到相应的对象查找问题。


    • 你可能想看:

      扫描二维码推送至手机访问。

      版权声明:本文由皇冠云发布,如需转载请注明出处。

      本文链接:https://www.idchg.com/info/21708.html

      分享给朋友:

      “我的世界java内存参数” 的相关文章

      探索美国冷门VPS:高性价比与个性化服务的优选

      在谈论VPS(虚拟专用服务器)时,人们往往会联想到那些知名的品牌和服务,而美国冷门VPS市场却是一个值得关注的领域。这些冷门VPS提供商虽然在整体市场中的知名度较低,但却为特定的用户群体和需求提供了颇具价值的服务。我在研究这个市场时,发现不少提供商在某些方面有着相当的优势,让我对这个冷门领域充满了好...

      宝塔安装全攻略:轻松管理你的服务器与网站

      宝塔面板,凭借其简单易用的特性,已经成为很多用户搭建和管理网站的首选工具。作为一款开源的服务器管理软件,宝塔面板提供了丰富的功能和灵活的操作方式,让无论是新手还是经验丰富的用户都能轻松上手。我在使用宝塔面板的过程中,深刻体会到它带来的便利和高效。 功能与特点 宝塔面板最大的一大优势在于其直观的用户界...

      Zgo VPS:高性能虚拟专用服务器的最佳选择

      在2021年,ZgoCloud(最初名为Zgovps)如雨后春笋般成立于美国特拉华州。作为一家新兴的技术公司,我们专注于提供高性能的虚拟专用服务器(VPS),这让我对公司的前景充满了期待。我们最初的使命是为各种用户提供可靠的网络解决方案,而现在我们已经成长为行业内的一股重要力量。 我们的全球数据中心...

      低价VPS: 如何选择最合适的虚拟私人服务器

      低价VPS概述 我们常常听到VPS这个词,它代表“虚拟私人服务器”。对于那些不太了解的人,VPS实际上是一种将一台物理服务器划分为多个虚拟服务器的技术。每个VPS都有独立的操作系统和存储空间,就像你在家里拥有一个独立的小房间一样。这样一来,你就能在不需要太多资金投入的情况下,拥有一台属于自己的服务器...

      RackNerd黑五促销活动详解:超值套餐与刺激抽奖

      RackNerd黑五促销活动概述 RackNerd,成立于2017年,以其出色的虚拟主机、VPS主机和独立服务器服务,在国外主机商领域占有一席之地。随着公司不断壮大,RackNerd在全球20个国家设立了数据中心,涵盖美加、欧洲和亚洲,提供多样化的服务方案。我一直很欣赏他们采用的当下流行的KVM虚拟...

      探索64clouds:方便的VPS管理工具及其功能解析

      什么是64clouds及其功能 在如今的云计算时代,64clouds无疑是一个让人耳目一新的名字。作为搬瓦工(BandwagonHost)VPS的KiwiVM控制面板域名,64clouds为用户提供了一个方便的管理平台。这意味着用户不必再为复杂的服务器管理技术而烦恼,64clouds的出现大大降低了...