草庐IT

java - 循环是否每次都需要更长的时间来执行?

coder 2024-03-06 原文

我正在从事一些涉及将邮政编码、城市和国家/地区存储在一起的 J2EE 项目。我们开发了一个 Java 类来处理每个国家/地区文件(包含每个邮政编码和每个城市)的集成。问题是对于某些国家(英国、荷兰...),文件非常大(400.000 到 800.000 行)。

我有一个 while() 循环,它读取下一行,获取信息并将其存储到我的数据库中。问题是,对于 1000 或 10.000 行的第一行,过程很快,非常快,然后每次循环时似乎都在变慢,然后恰好在 150.000 行后抛出 HeapSpaceOverflowException

我首先想到有些对象没有被垃圾回收并减慢了我的算法,但我无法弄清楚是哪一个。此外,当我在我的 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(), "");
}

通过@Inject注解注入(inject)变量this.pc

谁能帮我弄清楚为什么这段代码变得越来越慢?

为了完整起见,我添加了 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;
}

所以,我有一些更多的信息。 getCodePostal...() 方法在循环开始时需要大约 15 毫秒的时间来执行,在 10.000 行之后,需要超过 100 毫秒的时间来执行(几乎多 10 倍!)。 在这个新版本中,我禁用了提交/回滚代码,因此每个查询都是动态提交的。

我真的找不到为什么它需要越来越多的时间。

我试图搜索一些关于 JPA 缓存的信息: 我当前的配置是这样的(在 persistence.xml 中):

<property name="eclipselink.jdbc.bind-parameters" value="true"/>
<property name="eclipselink.jdbc.cache-statements" value="true"/>
<property name="eclipselink.cache.size.default" value="10000"/>
<property name="eclipselink.query-results-cache" value="true"/>

我不知道这是否是最有效的配置,我将不胜感激一些关于 JPA 缓存的帮助和解释。

最佳答案

您可能想要阅读 JPA 概念。简而言之,EntityManager 与持久性上下文相关联,持久性上下文保留对通过它操作的所有持久性对象的引用,因此它可以将对这些对象所做的任何更改写回数据库。

由于您从不关闭持久性上下文,这可能是内存泄漏的原因。此外,持久性提供者必须在发出查询之前将对持久对象的更改写入数据库,如果这些更改可能会改变查询的结果的话。要检测这些更改,需要迭代与当前持久上下文关联的所有对象。在您的代码中,您发出的每个查询都有将近一百万个对象。

因此,至少,您应该定期(比如每 1000 行)清除持久性上下文。

还值得注意的是,除非您的数据库位于同一台服务器上,否则您发出的每个查询都必须通过网络传输到数据库,然后将结果返回到应用程序服务器,然后您的程序才能继续。根据网络延迟,每次这很容易花费一毫秒 - 而您正在这样做几百万次。如果它需要真正高效,将整个表加载到内存中并执行内存中是否存在的检查,可能会快得多。

关于java - 循环是否每次都需要更长的时间来执行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24933492/

有关java - 循环是否每次都需要更长的时间来执行?的更多相关文章

  1. ruby - 我需要将 Bundler 本身添加到 Gemfile 中吗? - 2

    当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/

  2. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  3. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  4. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

  5. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  6. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  7. ruby - rspec 需要 .rspec 文件中的 spec_helper - 2

    我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只

  8. ruby - 如何在 Lion 上安装 Xcode 4.6,需要用 RVM 升级 ruby - 2

    我实际上是在尝试使用RVM在我的OSX10.7.5上更新ruby,并在输入以下命令后:rvminstallruby我得到了以下回复:Searchingforbinaryrubies,thismighttakesometime.Checkingrequirementsforosx.Installingrequirementsforosx.Updatingsystem.......Errorrunning'requirements_osx_brew_update_systemruby-2.0.0-p247',pleaseread/Users/username/.rvm/log/138121

  9. ruby - Chef 执行非顺序配方 - 2

    我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul

  10. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

随机推荐