Java 大体积目标的优化
我有一个从Oracle DB获取结果集的过程,然后使用返回的数据创建对象,然后将对象添加到映射,最后将映射加载到内存中的数据网格中。我的最大结果集可能有3000万条记录。我注意到,在处理了大约1400万条记录后,应用程序的速度明显减慢。我有-Xmx=12G和-Xms=512M。我的应用程序部署在WebLogic版本12.2.0.1和Java版本8_66上。我还注意到托管服务器产生的以下消息:Java 大体积目标的优化,java,performance,optimization,Java,Performance,Optimization,我有一个从Oracle DB获取结果集的过程,然后使用返回的数据创建对象,然后将对象添加到映射,最后将映射加载到内存中的数据网格中。我的最大结果集可能有3000万条记录。我注意到,在处理了大约1400万条记录后,应用程序的速度明显减慢。我有-Xmx=12G和-Xms=512M。我的应用程序部署在WebLogic版本12.2.0.1和Java版本8_66上。我还注意到托管服务器产生的以下消息: JVM暂停可能太长: 我试图弄清楚如何优化代码或JVM配置。这是密码 MyObj myObj =
MyObj myObj = null;
while (rs.next()) {
myObj = new Balance(rs.getString("Field1"), rs.getString("Field2"), rs.getString("Field3"),
....
rs.getString("Field17"), rs.getString("Field18"), rs.getString("Field19"));
Map<String, Account> myMap = new HashMap<>();
myMap.put(rs.getString("FieldA"), new Account(rs.getString("FieldA"), rs.getDouble("FieldC"),
rs.getString("FieldD"), Boolean.FALSE, Boolean.FALSE));
myObj.setAccounts(myMap);
myKey = myObj.getKey();
existingObject = cacheMap.get(myKey);
if (existingObject != null) {
myObj = myObj.merge(existingObject);
}
cacheMap.put(myKey, myObj);
recCount++;
if (recCount % 250000 == 0) {
logger.info("Processed " + recCount + " records.");
}
}
MyObj MyObj=null;
while(rs.next()){
myObj=新余额(rs.getString(“字段1”)、rs.getString(“字段2”)、rs.getString(“字段3”),
....
rs.getString(“Field17”)、rs.getString(“Field18”)、rs.getString(“Field19”);
Map myMap=newhashmap();
myMap.put(rs.getString(“FieldA”)、新账户(rs.getString(“FieldA”)、rs.getDouble(“FieldC”),
rs.getString(“FieldD”)、Boolean.FALSE、Boolean.FALSE);
myObj.setAccounts(myMap);
myKey=myObj.getKey();
existingObject=cacheMap.get(myKey);
if(existingObject!=null){
myObj=myObj.merge(现有对象);
}
cacheMap.put(myKey,myObj);
recCount++;
如果(记录%250000==0){
logger.info(“已处理”+recCount+“记录”);
}
}
您可以使用分页并分块检索数据集,然后您的程序可以在检索下一个数据块时开始处理当前数据块等等。以这种方式检索结果集所需的时间将减少。这里有一篇关于mySql分页的好文章选项:1
如果您的某些字段值将与字符串
相同,则在创建余额
和帐户
时,请执行字符串.intern()
。如果存在冗余值,这将给内存足迹带来巨大的好处
在我们的一个数据为2个LAC的应用程序中,我们看到String.intern()
之后有50%的好处
代码可能看起来有点难看。但是,通过一些实用的方法,你可以减少丑陋
代码在这里
String str(ResultSet rs, String fieldName)
{
String s = rs.getString(fieldName);
return s == null ? null : s.intern();
}
void somemethod()
{
...
...
while (rs.next()) {
myObj = new Balance(str(rs, "Field1"), str(rs, "Field2"), str(rs, "Field3"), str(rs, "Field17"), str(rs, "Field18"), str(rs, "Field19"));
Map<String, Account> myMap = new HashMap<>();
myMap.put(str(rs, "FieldA"), new Account(str(rs, "FieldA"), str(rs, "FieldC"), str(rs, "FieldD"), Boolean.FALSE, Boolean.FALSE));
myObj.setAccounts(myMap);
...
...
}
...
}
String str(结果集rs,字符串字段名)
{
字符串s=rs.getString(字段名);
返回s==null?null:s.intern();
}
void方法()
{
...
...
while(rs.next()){
myObj=新余额(str(rs,“字段1”)、str(rs,“字段2”)、str(rs,“字段3”)、str(rs,“字段17”)、str(rs,“字段18”)、str(rs,“字段19”);
Map myMap=newhashmap();
myMap.put(str(rs,“FieldA”)、新帐户(str(rs,“FieldA”)、str(rs,“FieldC”)、str(rs,“FieldD”)、Boolean.FALSE、Boolean.FALSE);
myObj.setAccounts(myMap);
...
...
}
...
}
选项:2
这真是一个节省内存的选项
如果使用Java 8 Update 20或更高版本,可以通过启用标志XX:+UseStringDuplication
为整个JVM启用此行为。如果您使用G1
GC,这将起作用
一些裁判。您可以搜索XX:+UseStringDuplication
以获取更多参考
选项:3
您真的可以考虑在
merge()
操作中执行的操作,以查看是否有任何功能更改来降低内存需求…是否有特定的原因需要在内存中使用如此大的映射?更好的策略是在需要的时候只取你需要的东西。这确实是一个设计问题。似乎12G对于您的程序来说是不够的,所以当垃圾收集器试图释放未使用的对象时,它会卡住,因为大多数对象仍在使用中。在任何情况下,如果重构太晚,您可以重新组织您的循环。我认为你可以改变你的merge
策略。即,首先在缓存映射中检查该对象,然后,如果该对象存在,则在现有映射中添加其他实体,而不是构建ne映射并将其与现有映射合并。我建议使用探查器检查运行时发生的情况。仅通过查看代码,我猜您可能的痛点是:rs#next
,myMap#put
,myObjsetAccounts
,myObj#merge
,cacheMap#put
。我已经在使用Statement.setFetchSize并找到了一个最佳点。这一定是另外一回事了第4节:因为这个原因而切换到Java9。