Java:clear()是否有助于快速垃圾收集?

sigwle7e  于 2023-05-21  发布在  Java
关注(0)|答案(4)|浏览(231)

从数据库1加载150万条记录
从 db2 加载150万条记录

List<DannDB> dDb = fromNamedQuery(); //return em.createNamedQuery("").getResultList();
List<LannDB> lDb = fromNamedQuery();

比较其数据。
更新/保存到数据库(使用JPA)
两小时后节目结束。
同样的迭代每三个小时发生一次,并且多次给予内存不足。
下面的语句是否有效,对象是否超出了此范围?

dDb.clear();

  or 

 dDb = null

或者我还能做什么

cedebl8k

cedebl8k1#

假设您的目标是减少OOME的发生,而不是考虑所有其他因素...
null分配给List对象将使整个列表符合垃圾收集的条件。然后,您需要创建一个新的(可能是空的)列表来替换它。
调用clear()将有一个 * 类似 * 的效果1空和重新创建,但细节将取决于List的实现。(例如,在ArrayList上调用clear()不会释放后台数组。它只会使数组单元格为空。)
如果您可以回收一个ArrayList,得到一个与原始列表大小大致相同的列表,那么就可以在增长列表时避免垃圾。(但我们不知道这是一个ArrayList!)
您的用例中的另一个因素是:

List<DannDB> dDb = fromNamedQuery();

无论如何都将创建一个新列表。这将使clear()变得毫无意义。(只需将null分配给dDb,或者让变量超出范围或重新分配给新列表。
最后一个问题是,可以想象该清单是可以最后确定的。这可能意味着删除列表对象需要更长的时间。
总的来说,我不能说分配null和调用clear()哪一个对内存占用更好。或者说,这两种情况中的任何一种都会产生重大影响。但是没有理由你不能尝试两种选择,并观察会发生什么。
我唯一能建议的是:

  • 增加堆大小(和RAM占用空间)。
  • 更改应用程序,以便不需要在内存中保存整个数据库快照。根据比较的性质,您可以在“块”中进行比较,也可以通过流式记录进行比较2。

最后一个是唯一可扩展的解决方案;即,其将对更大数量的记录起作用。(对处理更多记录所需的时间取模。)

重要提示:

1.手动运行System.gc() * 不太可能 * 有帮助。最好的情况下,它会(只是)使你的应用程序变慢。
1.由于真实的的问题是您得到了OOME,因此任何试图让JVM通过将内存返回给操作系统来缩小堆的做法都将适得其反。
1 -从存储管理的Angular 看类似。显然,清除列表和创建新列表之间存在语义差异;例如,如果您的应用程序的某些其他部分引用了原始列表。
2 -那些年龄足够大的人会记得用磁带存储实现工资单系统的经典方法。如果您可以从两个数据源中以相同的键顺序进行选择,则可以使用经典方法来比较它们。例如,并行阅读两个结果集。

ddarikpa

ddarikpa2#

对于SQL,可以获取两个[ResultSet](https://docs.oracle.com/javase/8/docs/api/java/sql/ResultSet.html)并迭代地比较它们的数据。这样,您就不必首先保存所有数据。
出于演示目的,我假设您的数据如下所示:
| 字符串电子邮件1|字符串电子邮件2| int someInt|
| --------------|--------------|--------------|
| abc@def.ghi | jkl@mno.pqr |1234567|
| xyz@gmail.com | | 8901234 |
要检测此数据库的两个ResultSet之间的差异,请执行以下操作:

boolean equals(ResultSet a, ResultSet b) {
    while(a.next() && b.next()) {
        String aEmail1 = a.getString(1);
        String bEmail1 = b.getString(1);
        if(!aEmail1.equals(bEmail1)) return false;
        String aEmail2 = a.getString(2);
        String bEmail2 = b.getString(2);
        if(!aEmail2.equals(bEmail2)) return false;
        int aSomeInt = a.getInt(3);
        int bSomeInt = b.getInt(3);
        if(aSomeInt!=bSomeInt) return false;
        if(a.isLast()!=b.isLast())
            throw new IllegalArgumentException(
                "ResultSets have different amounts of rows!"
            );
    }
    return true;
}

要将ResultSet oldData的内容(及其对应的数据库连接)设置为ResultSet newData

void updateA(ResultSet oldData, ResultSet newData) {
    while(oldData.next() && newData.next()) {
        String newEmail1 = newData.getString(1);
        oldData.updateString(1,newEmail1);
        String newEmail2 = newData.getString(2);
        oldData.updateString(2,newEmail2);
        int newSomeInt = newData.getInt(3);
        oldData.updateInt(3,newSomeInt);
        if(oldData.isLast()!=newData.isLast())
            throw new IllegalArgumentException(
                "ResultSets have different amounts of rows!"
            );
    }
}

当然,如果你不在乎这两个集合的行数不同,你可以省略if(a.isLast()!=newData.isLast)) ...if(oldData.isLast()!=newData.isLast()) ...

jhiyze9q

jhiyze9q3#

问题是,默认情况下,一旦分配了堆内存大小就不会缩小(我指的是从操作系统分配的内存大小)。如果您的Java应用程序曾经需要2GB的RAM,默认情况下,它将保留操作系统的RAM。
如果可以的话,请尝试更改应用程序的设计,不要首先将所有数据加载到内存中,而只加载工作真正需要的数据。
如果您确实需要同时执行两个大批处理,请考虑使用以下Java命令行参数:“-XX:+UseAdaptiveSizePolicy”,这将使在大量内存使用后缩小堆空间成为可能。
您还可以通过“System.gc();“,但是a)在没有建议的命令行参数的情况下不会收缩分配的堆内存,b)真的,您不应该考虑这个问题。Java会自己运行它。
编辑:改进了我的第一个解释一点。

ymdaylpp

ymdaylpp4#

对于内存使用来说,最好的方法是列表不超出作用域。因此,最好(内存方面)逐个修改内容,只保留一个临时条目对象,而不是整个其他列表。
因此,您可以创建getNextFromNamedQuery()hasNextInNamedQuery()方法,并将数据设置为当前索引。
例如:

int i=0;
while(hasNextInNamedQuery()) {
    if(dDb.size()<=i) dDb.add(getNextFromQuery());
    else dDb.set(i,getNextFromQuery());
    i++;
}

相关问题