public class LogSql {
static final Set instants = new LinkedHashSet();
static String getSql() {
for (String sql:instants) {
if(1==1) return sql;
}
}
public LogSql() {
synchronized (instances) {//Exception throwed
instances.add(this);
}
}
}
在垃圾收集的时候我会在synchronized (instances)这个地方得到
ConcurrentModificationException
有兴趣的讨论一下为什么
没兴趣的希望不要灌水,让它沉下去就可以,而且我给得分也不多
26 个解决方案
#1
当使用 fail-fast iterator 对 Collection 或 Map 进行迭代操作过程中尝试直接修改 Collection / Map 的内容时,
即使是在单线程下运行, java.util.ConcurrentModificationException 异常也将被抛出。 Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。
有意思的是如果你的 Collection / Map 对象实际只有一个元素的时候, ConcurrentModificationException 异常并不会被抛出。这也就是为什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。
有意思的是如果你的 Collection / Map 对象实际只有一个元素的时候, ConcurrentModificationException 异常并不会被抛出。这也就是为什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.
#2
PS: 转的..共同学习. 以前都没见过ConcurrentModificationException 这个异常..呵呵
#3
GC 的工作是不可预知的,你是怎么知道是在垃圾收集的时候产生的?
#4
up我也想知道3楼这个问题
#5
GC 的工作是不可预知的...
#6
火龙果来了,首先欢迎。
是不是真的垃圾收集,只能说八九不离十/
class LogSql是spring管理的。比如
这样配置<bean id="javax.sql.logSql" class="LogSql" ......
那么我在log中看到
Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans
javax.sql.logSql
这句话的话,基本可以确定它是在载入这个类,应用没有停止的情况下重新载入一个类
我觉得很可能就是垃圾收集
当然我说的不准确,其实是垃圾收集之后再次执行时出的错
#7
有几个基本点,希望大家发表看法
1,static Set变量会不会被垃圾收集
2,如果不会,那么它的内容会不会被收集
3,如果他的内容,比如instances中的一个对象被收集了那么它的容量就变小了吗?
4,如果他的内容被收集,索引会不会被更新
5, 类的重新载入静态域会被重置吗
6,看到了类重新载入就意味着它的所有实例都改变了吗?
7,还是说只有实例都销毁之后才能重新载入类
1,static Set变量会不会被垃圾收集
2,如果不会,那么它的内容会不会被收集
3,如果他的内容,比如instances中的一个对象被收集了那么它的容量就变小了吗?
4,如果他的内容被收集,索引会不会被更新
5, 类的重新载入静态域会被重置吗
6,看到了类重新载入就意味着它的所有实例都改变了吗?
7,还是说只有实例都销毁之后才能重新载入类
#8
public class LogSql {
static final Set instants = new LinkedHashSet();
static String getSql() {
for (String sql:instants) {
if(1==1) return sql;
}
}
public LogSql() {
synchronized (instances) {//Exception throwed
instances.add(this);
}
}
}
很难想象这段代码能通过编译
#9
import java.util.LinkedHashSet;
import java.util.Set;
public class LogSql {
static final Set<LogSql> instants = new LinkedHashSet<LogSql>();
private static LogSql defaultLogSql;
static LogSql getSql() {
for (LogSql sql:instants) {
if(1==1) return sql;
}
return defaultLogSql;
}
public LogSql() {
synchronized (instants) {//Exception throwed
instants.add(this);
}
}
}
import java.util.Set;
public class LogSql {
static final Set<LogSql> instants = new LinkedHashSet<LogSql>();
private static LogSql defaultLogSql;
static LogSql getSql() {
for (LogSql sql:instants) {
if(1==1) return sql;
}
return defaultLogSql;
}
public LogSql() {
synchronized (instants) {//Exception throwed
instants.add(this);
}
}
}
#10
上边的应该可以通过编译,它只是一段伪代码
#11
看不出这段代码这样做有什么特殊的意义。
所谓的垃圾回收是指某个对象没有被引用时,那这个对象就有可能会被回收掉,
Set 中的对象在 Set 中被引用了,不会被垃圾回收掉的。
所谓的垃圾回收是指某个对象没有被引用时,那这个对象就有可能会被回收掉,
Set 中的对象在 Set 中被引用了,不会被垃圾回收掉的。
#12
JLS允许类被卸载。
JDK1.1.2中卸载产生了一个不好的结果,就是一个类的静态量会被重新初始化。他和JLS8.3.1.中对于静态的描述(不管多少个实例被创建静态量只有一个)相冲突。类加载一般有两种途径。一种是类A中声明了类B,那么在执行到声明时就会加载类B.根据JLS卸载类时有几个限制。1,类实例能找到的话,类不能被卸载 2,类的lass对象能找到的话,类不能被卸载.如果以各类是用forname方法加载的,那么在没有对其的引用的情况下他就是不可到达的。相反,如果类是因为有一个引用链到它尔加载的话,那么那还是可到达的。就是说即使类没有了实例,也没有Class对象引用到它,它也还是可到达的。
要卸载一个类还应该需要有一个条件,如果加载类的类加载器还存在那么不要卸载含有静态量的类。
JDK1.1.2中卸载产生了一个不好的结果,就是一个类的静态量会被重新初始化。他和JLS8.3.1.中对于静态的描述(不管多少个实例被创建静态量只有一个)相冲突。类加载一般有两种途径。一种是类A中声明了类B,那么在执行到声明时就会加载类B.根据JLS卸载类时有几个限制。1,类实例能找到的话,类不能被卸载 2,类的lass对象能找到的话,类不能被卸载.如果以各类是用forname方法加载的,那么在没有对其的引用的情况下他就是不可到达的。相反,如果类是因为有一个引用链到它尔加载的话,那么那还是可到达的。就是说即使类没有了实例,也没有Class对象引用到它,它也还是可到达的。
要卸载一个类还应该需要有一个条件,如果加载类的类加载器还存在那么不要卸载含有静态量的类。
#13
多线程情况下,GC引起的Set重新初始化,会引起异常
所以如果这个类不是单态的,那么就应该考虑将其所有操作同期化
但Web*同部分的同期话会带来性能的牺牲
所以最好的方式是将其单态化
所以如果这个类不是单态的,那么就应该考虑将其所有操作同期化
但Web*同部分的同期话会带来性能的牺牲
所以最好的方式是将其单态化
#14
回到这里,显然static量会因为Class被GC而产生状态的改变
如果其内容的引用不被收集那么内容就不应该会改变
索引的更新是单线程的这使得在多线程情况下,应对Set的索引操作也作同步但会作出性能牺牲
卸载类时有几个限制。1,类实例能找到的话,类不能被卸载 2,类的lass对象能找到的话,类不能被卸载.
不满足以上两条那么类本身也会被GC
#15
既然能运行到这个代码,说明LogSql所在的对象存在,(不存在对象就报NULL了)
既然对象存在,又怎么可能会被GC
LZ好好查查自己的代码其他的位置
#16
说到这里基本上就解决问题了
改天把这套配置整理一下
改天把这套配置整理一下
#17
没遇到过
#18
遇到一个问题就是spring 的单态不起作用
我有一个web应用和几个webservice在webservice和web应用中都配置LogSql
并且singleton为true
但是跟踪发现有多个对象
我有一个web应用和几个webservice在webservice和web应用中都配置LogSql
并且singleton为true
但是跟踪发现有多个对象
#19
去掉static并且非单态的话对效率影响太大
但是单态又不能解决
回到了如何让类不被回收这个问题上
但是单态又不能解决
回到了如何让类不被回收这个问题上
#20
如果加一个参数可以做到类不被回收
java -Xnoclassgc xxxxxxx
java -Xnoclassgc xxxxxxx
#21
由于同时在web和web service用这个类,所以跟踪了一下。
发现每次从web service中,加在spring配置文件时,类实例都加一。
查spring配置文件发现被配置为单态。也就是说类伴随着spring容器的加载而增加。
于是把web service用的spring配置文件改到web初始化时加载一次。
这样类实例就只剩下了两个。
这样推定原因为
由于每次spring加载那么实例增加,静态set的size增加。
如果一个线程访问过程中由于gc,set被重置那么就会发生异常
发现每次从web service中,加在spring配置文件时,类实例都加一。
查spring配置文件发现被配置为单态。也就是说类伴随着spring容器的加载而增加。
于是把web service用的spring配置文件改到web初始化时加载一次。
这样类实例就只剩下了两个。
这样推定原因为
由于每次spring加载那么实例增加,静态set的size增加。
如果一个线程访问过程中由于gc,set被重置那么就会发生异常
#22
上个周五做了一天试验。
启动web Server加载应用,然后不断去执行web service.
使得LogSql 的instances不断增加。
其间强制垃圾收集两次。但LogSql均未被收集掉。
这个事实与预期相悖
启动web Server加载应用,然后不断去执行web service.
使得LogSql 的instances不断增加。
其间强制垃圾收集两次。但LogSql均未被收集掉。
这个事实与预期相悖
#23
本来打算将我的心得写上来,不过看来也没人看,算了,继续自言自语
由于LogSql对象一个都没被收集掉,可以确认他被某个实力引用到了。
暂时决定将LogSql做成真正的单态
由于LogSql对象一个都没被收集掉,可以确认他被某个实力引用到了。
暂时决定将LogSql做成真正的单态
#24
你虽然在往里加的时候同步了,获取的时候也应该同步,因为你往里加的时候同时获取,本来数据就会出现异常,先不说JDK那"行话".你在foreache循环中get的时候往里加一个对象,这里下面的循环还会正确吗?如果仅仅是getXXXat(i)这种非迭代操作,也就是非foreache倒是可以的.
#25
这里主要考虑是效率的问题。所以虽然有时会出错但由于是log所以一直就没改过。
而且当初的考虑是它是单态的所以没有问题,没想到spring的单态是假单态
现在就是搞不清到底原因是什么,到底是gc引起出错还是add引起出错。
正在考虑将其单态化。
#26
确定是在add的时候出错。
线程一进入循环
线程二把set数加一
线程一继续循环, 出错
线程一进入循环
线程二把set数加一
线程一继续循环, 出错
#1
当使用 fail-fast iterator 对 Collection 或 Map 进行迭代操作过程中尝试直接修改 Collection / Map 的内容时,
即使是在单线程下运行, java.util.ConcurrentModificationException 异常也将被抛出。 Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。
有意思的是如果你的 Collection / Map 对象实际只有一个元素的时候, ConcurrentModificationException 异常并不会被抛出。这也就是为什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。
有意思的是如果你的 Collection / Map 对象实际只有一个元素的时候, ConcurrentModificationException 异常并不会被抛出。这也就是为什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.
#2
PS: 转的..共同学习. 以前都没见过ConcurrentModificationException 这个异常..呵呵
#3
GC 的工作是不可预知的,你是怎么知道是在垃圾收集的时候产生的?
#4
up我也想知道3楼这个问题
#5
GC 的工作是不可预知的...
#6
火龙果来了,首先欢迎。
是不是真的垃圾收集,只能说八九不离十/
class LogSql是spring管理的。比如
这样配置<bean id="javax.sql.logSql" class="LogSql" ......
那么我在log中看到
Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans
javax.sql.logSql
这句话的话,基本可以确定它是在载入这个类,应用没有停止的情况下重新载入一个类
我觉得很可能就是垃圾收集
当然我说的不准确,其实是垃圾收集之后再次执行时出的错
#7
有几个基本点,希望大家发表看法
1,static Set变量会不会被垃圾收集
2,如果不会,那么它的内容会不会被收集
3,如果他的内容,比如instances中的一个对象被收集了那么它的容量就变小了吗?
4,如果他的内容被收集,索引会不会被更新
5, 类的重新载入静态域会被重置吗
6,看到了类重新载入就意味着它的所有实例都改变了吗?
7,还是说只有实例都销毁之后才能重新载入类
1,static Set变量会不会被垃圾收集
2,如果不会,那么它的内容会不会被收集
3,如果他的内容,比如instances中的一个对象被收集了那么它的容量就变小了吗?
4,如果他的内容被收集,索引会不会被更新
5, 类的重新载入静态域会被重置吗
6,看到了类重新载入就意味着它的所有实例都改变了吗?
7,还是说只有实例都销毁之后才能重新载入类
#8
public class LogSql {
static final Set instants = new LinkedHashSet();
static String getSql() {
for (String sql:instants) {
if(1==1) return sql;
}
}
public LogSql() {
synchronized (instances) {//Exception throwed
instances.add(this);
}
}
}
很难想象这段代码能通过编译
#9
import java.util.LinkedHashSet;
import java.util.Set;
public class LogSql {
static final Set<LogSql> instants = new LinkedHashSet<LogSql>();
private static LogSql defaultLogSql;
static LogSql getSql() {
for (LogSql sql:instants) {
if(1==1) return sql;
}
return defaultLogSql;
}
public LogSql() {
synchronized (instants) {//Exception throwed
instants.add(this);
}
}
}
import java.util.Set;
public class LogSql {
static final Set<LogSql> instants = new LinkedHashSet<LogSql>();
private static LogSql defaultLogSql;
static LogSql getSql() {
for (LogSql sql:instants) {
if(1==1) return sql;
}
return defaultLogSql;
}
public LogSql() {
synchronized (instants) {//Exception throwed
instants.add(this);
}
}
}
#10
上边的应该可以通过编译,它只是一段伪代码
#11
看不出这段代码这样做有什么特殊的意义。
所谓的垃圾回收是指某个对象没有被引用时,那这个对象就有可能会被回收掉,
Set 中的对象在 Set 中被引用了,不会被垃圾回收掉的。
所谓的垃圾回收是指某个对象没有被引用时,那这个对象就有可能会被回收掉,
Set 中的对象在 Set 中被引用了,不会被垃圾回收掉的。
#12
JLS允许类被卸载。
JDK1.1.2中卸载产生了一个不好的结果,就是一个类的静态量会被重新初始化。他和JLS8.3.1.中对于静态的描述(不管多少个实例被创建静态量只有一个)相冲突。类加载一般有两种途径。一种是类A中声明了类B,那么在执行到声明时就会加载类B.根据JLS卸载类时有几个限制。1,类实例能找到的话,类不能被卸载 2,类的lass对象能找到的话,类不能被卸载.如果以各类是用forname方法加载的,那么在没有对其的引用的情况下他就是不可到达的。相反,如果类是因为有一个引用链到它尔加载的话,那么那还是可到达的。就是说即使类没有了实例,也没有Class对象引用到它,它也还是可到达的。
要卸载一个类还应该需要有一个条件,如果加载类的类加载器还存在那么不要卸载含有静态量的类。
JDK1.1.2中卸载产生了一个不好的结果,就是一个类的静态量会被重新初始化。他和JLS8.3.1.中对于静态的描述(不管多少个实例被创建静态量只有一个)相冲突。类加载一般有两种途径。一种是类A中声明了类B,那么在执行到声明时就会加载类B.根据JLS卸载类时有几个限制。1,类实例能找到的话,类不能被卸载 2,类的lass对象能找到的话,类不能被卸载.如果以各类是用forname方法加载的,那么在没有对其的引用的情况下他就是不可到达的。相反,如果类是因为有一个引用链到它尔加载的话,那么那还是可到达的。就是说即使类没有了实例,也没有Class对象引用到它,它也还是可到达的。
要卸载一个类还应该需要有一个条件,如果加载类的类加载器还存在那么不要卸载含有静态量的类。
#13
多线程情况下,GC引起的Set重新初始化,会引起异常
所以如果这个类不是单态的,那么就应该考虑将其所有操作同期化
但Web*同部分的同期话会带来性能的牺牲
所以最好的方式是将其单态化
所以如果这个类不是单态的,那么就应该考虑将其所有操作同期化
但Web*同部分的同期话会带来性能的牺牲
所以最好的方式是将其单态化
#14
回到这里,显然static量会因为Class被GC而产生状态的改变
如果其内容的引用不被收集那么内容就不应该会改变
索引的更新是单线程的这使得在多线程情况下,应对Set的索引操作也作同步但会作出性能牺牲
卸载类时有几个限制。1,类实例能找到的话,类不能被卸载 2,类的lass对象能找到的话,类不能被卸载.
不满足以上两条那么类本身也会被GC
#15
既然能运行到这个代码,说明LogSql所在的对象存在,(不存在对象就报NULL了)
既然对象存在,又怎么可能会被GC
LZ好好查查自己的代码其他的位置
#16
说到这里基本上就解决问题了
改天把这套配置整理一下
改天把这套配置整理一下
#17
没遇到过
#18
遇到一个问题就是spring 的单态不起作用
我有一个web应用和几个webservice在webservice和web应用中都配置LogSql
并且singleton为true
但是跟踪发现有多个对象
我有一个web应用和几个webservice在webservice和web应用中都配置LogSql
并且singleton为true
但是跟踪发现有多个对象
#19
去掉static并且非单态的话对效率影响太大
但是单态又不能解决
回到了如何让类不被回收这个问题上
但是单态又不能解决
回到了如何让类不被回收这个问题上
#20
如果加一个参数可以做到类不被回收
java -Xnoclassgc xxxxxxx
java -Xnoclassgc xxxxxxx
#21
由于同时在web和web service用这个类,所以跟踪了一下。
发现每次从web service中,加在spring配置文件时,类实例都加一。
查spring配置文件发现被配置为单态。也就是说类伴随着spring容器的加载而增加。
于是把web service用的spring配置文件改到web初始化时加载一次。
这样类实例就只剩下了两个。
这样推定原因为
由于每次spring加载那么实例增加,静态set的size增加。
如果一个线程访问过程中由于gc,set被重置那么就会发生异常
发现每次从web service中,加在spring配置文件时,类实例都加一。
查spring配置文件发现被配置为单态。也就是说类伴随着spring容器的加载而增加。
于是把web service用的spring配置文件改到web初始化时加载一次。
这样类实例就只剩下了两个。
这样推定原因为
由于每次spring加载那么实例增加,静态set的size增加。
如果一个线程访问过程中由于gc,set被重置那么就会发生异常
#22
上个周五做了一天试验。
启动web Server加载应用,然后不断去执行web service.
使得LogSql 的instances不断增加。
其间强制垃圾收集两次。但LogSql均未被收集掉。
这个事实与预期相悖
启动web Server加载应用,然后不断去执行web service.
使得LogSql 的instances不断增加。
其间强制垃圾收集两次。但LogSql均未被收集掉。
这个事实与预期相悖
#23
本来打算将我的心得写上来,不过看来也没人看,算了,继续自言自语
由于LogSql对象一个都没被收集掉,可以确认他被某个实力引用到了。
暂时决定将LogSql做成真正的单态
由于LogSql对象一个都没被收集掉,可以确认他被某个实力引用到了。
暂时决定将LogSql做成真正的单态
#24
你虽然在往里加的时候同步了,获取的时候也应该同步,因为你往里加的时候同时获取,本来数据就会出现异常,先不说JDK那"行话".你在foreache循环中get的时候往里加一个对象,这里下面的循环还会正确吗?如果仅仅是getXXXat(i)这种非迭代操作,也就是非foreache倒是可以的.
#25
这里主要考虑是效率的问题。所以虽然有时会出错但由于是log所以一直就没改过。
而且当初的考虑是它是单态的所以没有问题,没想到spring的单态是假单态
现在就是搞不清到底原因是什么,到底是gc引起出错还是add引起出错。
正在考虑将其单态化。
#26
确定是在add的时候出错。
线程一进入循环
线程二把set数加一
线程一继续循环, 出错
线程一进入循环
线程二把set数加一
线程一继续循环, 出错