我正在研究一些涉及将邮政编码,城市和国家存储在一起的J2EE项目.我们开发了一个Java类来处理每个国家文件的集成(包含每个邮政编码和每个城市).问题是,对于一些国家(英国,荷兰......),文件非常紧凑(400,000到800.000行).
我有一个while()
循环读取下一行,获取信息并将其存储到我的数据库中.问题是,对于1000或10.000的第一行,这个过程很快,非常快,然后每次进入循环时似乎都在减慢,然后恰好抛出HeapSpaceOverflowException
150.000行之后.
我首先想到一些对象不是垃圾收集并且减慢了我的算法,但我无法弄清楚哪一个.此外,当我在我的PC上运行此算法时,JConsole告诉我堆空间定期清理(似乎是垃圾收集),但过程仍然更慢更慢...
以下是该方法的代码:
FileReader fr = new FileReader(nomFichier); BufferedReader br = new BufferedReader(fr); int index = 0; String ligne; String codePostal; String nomVille; String codePays; PPays pays; String[] colonnes; while ((ligne = br.readLine()) != null) { System.out.println("line "+ ++index); colonnes = ligne.split(Pattern.quote(";")); codePostal = colonnes[9]; nomVille = colonnes[8]; codePays = colonnes[0]; pays = this.pc.getByCodePays(codePays); this.pc.getByCodePostalAndVilleAndINSEE(codePostal, nomVille, pays.getNomPays(), ""); }
变量this.pc
通过@Inject注释注入.
有人可以帮我弄清楚为什么这段代码变得越来越慢?
非常感谢.
编辑:为了完成,我添加了get...()
方法的代码:
public Codepostalville getByCodePostalAndVilleAndINSEE(String codePostal, String ville, String pays, String codeINSEE) throws DatabaseException { Codepostal cp = null; Ville v = null; PPays p = null; Codepostalville cpv = null; try { // Tout d'abord, il faut retrouver l'objet CodePostal cp = (Codepostal) this.em .createNamedQuery("Codepostal.findByCodePostal") .setParameter("codePostal", codePostal) .getSingleResult(); } catch (NoResultException nre1) { // Si on ne l'a pas trouvé, on le crée if (cp == null) { cp = new Codepostal(); cp.setCodePostal(codePostal); cpc.getFacade().create(cp); } } // On retrouve la ville... try { // Le nom de la ville passé par l'utilisateur doit être purgé (enlever // les éventuels tirets, caractères spéciaux...) // On crée donc un nouvel objet Ville, auquel on affecte le nom à purger // On effectue la purge, et on récupère le nom purgé Ville purge = new Ville(); purge.setNomVille(ville); purge.purgerNomVille(); ville = purge.getNomVille(); v = (Ville) this.em .createNamedQuery("Ville.findByNomVille") .setParameter("nomVille", ville) .getSingleResult(); } catch (NoResultException nre2) { // ... ou on la crée si elle n'existe pas if (v == null) { v = new Ville(); v.setNomVille(ville); vc.getFacade().create(v); } } // On retrouve le pays try { p = (PPays) this.em .createNamedQuery("PPays.findByNomPays") .setParameter("nomPays", pays) .getSingleResult(); } catch (NoResultException nre2) { // ... ou on la crée si elle n'existe pas if (p == null) { p = new PPays(); p.setNomPays(pays); pc.getFacade().create(p); } } // Et on retrouve l'objet CodePostalVille try { cpv = (Codepostalville) this.em .createNamedQuery("Codepostalville.findByIdVilleAndIdCodePostalAndIdPays") .setParameter("idVille", v) .setParameter("idCodePostal", cp) .setParameter("idPays", p) .getSingleResult(); // Si on a trouvé l'objet CodePostalVille, on met à jour son code INSEE cpv.setCodeINSEE(codeINSEE); this.getFacade().edit(cpv); } catch (NoResultException nre3) { if (cpv == null) { cpv = new Codepostalville(); cpv.setIdCodePostal(cp); cpv.setIdVille(v); cpv.setCodeINSEE(codeINSEE); cpv.setIdPays(p); this.getFacade().create(cpv); } } return cpv; }
再次感谢.
编辑2:所以,我有更多的信息.该getCodePostal...()
方法在循环开始时需要大约15ms执行,在10.000行之后,需要执行超过100ms(几乎10倍!).在这个新版本中,我已经禁用了提交/回滚代码,因此每个查询都是动态提交的.
我无法真正找到为什么它需要越来越多的时间.
我试图搜索有关JPA缓存的一些信息:我当前的配置是这样的(在persistence.xml中):
我不知道它是否是最有效的配置,我希望得到一些关于JPA缓存的帮助和一些解释.
谢谢.
您可能想要阅读JPA概念.简而言之,EntityManager与持久化上下文相关联,该上下文保持对通过它操纵的所有持久对象的引用,因此它可以将对这些对象所做的任何更改写回数据库.
由于您从未关闭持久性上下文,这可能是导致内存泄漏的原因.此外,如果这些更改可能会更改查询结果,则持久性提供程序必须在发出查询之前将持久对象的更改写入数据库.要检测这些更改,需要迭代与当前持久上下文关联的所有对象.在您的代码中,您发出的每个查询都有近一百万个对象.
因此,至少应该定期清除持久化上下文(比如说每1000行).
还值得注意的是,除非您的数据库位于同一服务器上,否则您发出的每个查询都必须通过网络传输到数据库,并在程序继续之前将结果返回给应用程序服务器.根据网络延迟,每次可能很容易花费一毫秒 - 而且这样做几百万次.如果它需要真正有效,将整个表加载到内存中,并在那里执行检查,可能会大大加快.