标题说的不太详细
就是我的程序不断的往一个*.dat文件中追加手机号,一行一个,刚开始第一次运行的时候,没有文件,创建然后写
速度都很快,当我的文件越来越大,其实也不大,就100行左右,我要继续追加,然而速度就变得越来越慢
刚开始写200多个文件一共30-40ms,现在是300-500ms
这是为什么?
参考代码
监控中发现就这俩方法耗时间
void pickOne(File f, String fingerprint) {
long ts = System.currentTimeMillis();
try (FileInputStream fis = new FileInputStream(f)) {
String name = f.getName().split("\\.")[0];
PerfLogging.info("Loading ..... " + fingerprint + "/" + name + " from " + f.getAbsolutePath() + " ..... ");
BufferedReader buf = new BufferedReader(new InputStreamReader(fis));
String line = null;
while ((line = buf.readLine()) != null) {
addPhone(name, line);
}
buf.close();
ts = System.currentTimeMillis() - ts;
PerfLogging.info("time elapsed " + ts + "ms");
} catch (Exception e) {
e.printStackTrace();
}
}
public void dump(String basedir) {
File dir = new File(basedir, this.preHash);
for (Entry<String, Set<String>> en : this.hTab.entrySet()) {
String pre2 = en.getKey();
try (FileOutputStream fos = new FileOutputStream(new File(dir, pre2 + ".dat"))) {
for (String s : en.getValue()) {
fos.write((s + "\n").getBytes());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
这个代码不是我写的,我查到这个
java1.7特性,叫做try-with-resource,实现了AutoCloseable接口的实例可以放在try(...)中在离开try块时将自动调用close()方法。该方法调用可以看做在finally块中,所以资源的释放一定会执行,不过能不能成功释放还是得看close方法是否正常返回。
还有,我的服务器有40个G的内存,即使开给它还是很慢
我读取的是*.dat文件,不是二进制文件,可以理解成是txt类型的文件
我测试的时候,在第一次运行的时候,特别快,十分之一
但是多运行几次后,速度直线下降
看你们说了这么多,真是8仙过海,各显神通!我也说两句!
首先AutoCloseable是jdk1.7以上属性,会自动关闭io流!!!所以说楼上有人说会因为流没关闭的原因显然是不成立的!!!
楼上有人说文件大文件操作文件,小文件操作内存,其为可能原因!但是看楼主用了buffer缓冲流,所以此点也不是核心原因!
个人觉得,其最主要的核心原因是addPhone方法的内部实现算法,就如楼上所说,算法复杂度是怎样的,我觉得这是重中之重!
楼主可以单独检测addPhone方法,看其执行时间是不是递增的,个人觉得应该是线性递增.或指数递增,线性递增的可能性比较大!
因此在你文件越来越大的时候就会越来越慢!另外,个人觉得你的addPhone应该有临界值,过临界值可能算法复杂度会突然变化较大!
dump(String basedir) 中 FileOutputStream fos 没关闭导致的?
代码不是太全,你的方式是,先把之前文件中的内容全读出来,然后再全写进去?
文件小,因为缓存机制的存在,实际上你操作的是内存,所以快。文件大,你操作的就是文件了,速度低几个数量级。
另外addPhone怎么实现的,它的算法复杂度是线性的,还是高于线性的。
run() method贴出来看一看
FileOutputStream 文件输出流没关闭导致
要清理缓存哦,否则会越来越慢
个人见解:
try (FileOutputStream fos = new FileOutputStream(new File(dir, pre2 + ".dat"))) {
for (String s : en.getValue()) {
fos.write((s + "\n").getBytes());
}
} catch (Exception e) {
e.printStackTrace();
}
这一块的代码写法感觉有问题,fos不会在try块结束后自动close,需要显式调用fos.close()方法,不然打开的文件会因一直持有对象而无法被GC回收,存在内存泄露的风险。
换成Linux C下的写法,int fd = open(filepath, mode);文件操作会持有一个文件句柄,没有close该句柄就不会被回收,导致内存无法释放
而你IO读写变慢的原因可能就是可用内存逐渐减少,文件操作的缓存越来越少导致速度越来越慢。
可以修改为以下写法重新测试:
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File(dir, pre2 + ".dat"))
for (String s : en.getValue()) {
fos.write((s + "\n").getBytes());
}
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if (fos != null) {
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
IO操作类, try结束后需要显示调用finally在finally里面进行关闭操作,如果没有进行关闭会有内存泄露问题,时间一久就会出现卡顿问题,用close()
方法关闭
检查一下是否有内存泄露
资源用完,就需要归还,就像种地,你种完庄稼后,不去处理,下次这块地就用不了了
最好不要一行一行的写,通过把需要写的数据拼接成一个字符串,然后一次性写入文件中,速度会快很多。
我也遇到相同的问题了 而且问题很诡异 同样的append的写入方式,在I7台式机 + 机械硬盘 写完文件需要3个小时
但是在mac 上只是需要30秒 所以感觉跟硬盘关系很大
但是后来在台式机上做测试,并不是所有的文件写入都很慢,单独是程序生成的这个文件在追加时比较慢
看代码没发现问题 所以手动重写了Files的write方法 并且手动关闭流 发现慢的瓶颈在流关闭的时候
令我不解的是 如果将写入慢的这个文件用文件编辑器打开 随便再写入个东西保存 之后再写数据就不会慢了......