由一个私有静态变量的处理问题谈java内存模型
来源:互联网 发布:mac如何查看qq群文件 编辑:程序博客网 时间:2024/06/05 15:46
近期,在项目中遇到一个问题,项目中与其他系统通信的代码,在其他系统已经停掉的情况下,程序中的返回值仍然有信息。经过验证,产生该现象的实际操作为:应用与其他系统通信——其他系统关闭(即其他系统不返回信息)——应用再次通信——返回值存在信息。由于本身我们的项目中,发送通信信息的代码是一个封装好的架包,而该架包的源码已经丢失,不得以,只好反编译源码以后进行调试。最终确认问题根源:返回值是使用架包中某个类的私有静态变量存储的,故每次发送信息成功后,该变量中均会缓存上次成功返回的信息。而当程序收不到返回信息时,并没有将原有缓存信息全部处理掉。
后来虽然通过外部判断条件优化的形式处理了上述问题,但实际上仍然未从根源上处理掉这个问题。当然,后来我修改了架包中的代码,将该问题处理掉了。但是处理过程中发现,如果想顺利解决该问题,需要对jvm中的内存模型有一定了解才行。下面我就简单介绍一下处理这个问题时遇到的问题,随后补充总结jvm中的内存分配。
首先将通讯程序中信息发送公共类中私有静态变量的代码贴出:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
上面的代码,其中最recv的字节数组是最终返回应用的从其他系统接收到的信息,而这里的信息是TcpSend.send()方法返回的。这个方法的具体内容是这样的:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
我们可以看到,除了getField_value这个map类型的变量外,其他变量均是固定值;而getField_value这个变量所存放的正是每次其他应用系统正常通讯返回的信息。
下面我们看一下实际通讯的代码(由于通讯方法代码很长,这里我只截取部分代码):
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
我们仔细分析这两段代码,第一段代码中,异常情况判断中,如果recv为空(即收到的返回值是空),getField_value这个map型变量赋值,然后返回;而超时未收到返回值会在map中赋入一个timeout的键值对。而实际上,我们分析接收返回报文的第二段代码可以看出,返回到第一段代码中的recv变量是null;此时,程序会对最终返回应用的getField_value这个map中赋一个值为X00B名为12的键值对,而上一次报文发送成功后,返回的其他键值对信息,由于getField_value变量无法释放,也一直没有清空,所以上一次发送信息中,除了名为12的键值对被覆盖,其他的键值对仍然存在。故应用中如果取其他的键值对信息,最终就会造成误判。
相应的解决方案很容易,只要我们在第一段代码的Tcp_8583()方法起始位置将getField_value变量清空即可,即新增该行代码getField_value.clear();
如果想要迅速解决这个问题,需要理解私有静态变量及其类在jvm中释放的实际及相应的存储位置,否则在面对这个问题时,会十分迷茫,感到无从下手;只有正确分析出,私有静态变量在jvm的静态域中存储以供随时调用,且私有静态变量可以在类相应的对象未创建时使用。
附:
内存模型:
1、 寄存器。这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部。然而,寄存器的数量十分有限,所以寄存器是根据需要由编译器分配。我们对此没有直接的控制权,也不可能在自己的程序里找到寄存器存在的任何踪迹。即我们在程序中无法控制。
2、 栈,又称堆栈。用于存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中。驻留于常规RAM(随机访问存储器)区域。但可通过它的“堆栈指针”获得处理的直接支持。堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存方式,仅次于寄存器。创建程序时,Java编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活性,所以尽管有些java数据要保存在堆栈里——特别是对象句柄,但java对象并不放到其中。
3、 堆。一种常规用途的内存池(也在RAM区域),其中保存了java对象,即用new产生的数据。和堆栈不同:“内存堆”或“堆”最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new命令编制相碰的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间这里写代码片时会花掉更长的时间
4、 静态域。用于存放在对象中用static定义的静态成员。这儿的“静态”是指“位于固定位置”。程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。但java对象本身永远都不会置入静态存储空间。
5、常量池。用于存放常量。常数值通常直接置于程序代码内部。这样做是安全的。因为它们永远都不会改变,有的常数需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。
6、 非RAM存储。指硬盘等永久存储空间。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给另一台机器,而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对于这些类型的数据存储,一个特别有用的技艺就是它们能存在于其他媒体中,一旦需要,甚至能将它们恢复成普通的、基于RAM的对象。
另外需要说明的是字符串是一个特殊包装类,其引用是存放在栈里的,而对象内容必须根据创建方式不同定(常量池和堆).有的是编译期就已经创建好,存放在字符串常 量池中,而有的是运行时才被创建.使用new关键字,存放在堆中。
- 由一个私有静态变量的处理问题谈java内存模型
- 由一个私有静态变量的处理问题谈java内存模型
- 有关java私有变量访问的一个问题!!
- 一个静态变量的问题
- 私有的静态成员变量
- 由volatile关键字谈Java内存模型
- 由volatile关键字谈Java内存模型
- Java中关于静态变量的一个问题
- 单例模式简介及由它引出的JAVA局部静态变量问题
- jvm住内存与工作内存,内存模型反映的java多线程问题,volatile修饰变量
- 关于静态变量的内存分配问题
- 从一个修改私有变量的问题想到的
- 从一个修改私有变量的问题想到的
- 从一个修改私有变量的问题想到的
- C++ 的一个问题的理解(私有变量成员)
- C++ 的一个问题的理解(私有变量成员)
- Mage.php里面的变量(只有一个不是私有静态变量)
- Java String 变量的内存模型
- 排序
- swift中的for循环
- RxJava使用笔记
- RecyclerView子View不刷新(RequestLayout无效),RecyclerView.mEatRequestLayout
- Oracle监听的常用命令及监听日志分析
- 由一个私有静态变量的处理问题谈java内存模型
- API文档自动生成工具
- 461. Hamming Distance
- 密码算法
- 深刻理解HDFS工作机制
- LruCache缓存
- Vuex 模块化与项目实例 (2.0)
- 【C语言简单说】十:小结
- Java IO流