java中频繁的queue里添加数据没问题吧
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中频繁的queue里添加数据没问题吧相关的知识,希望对你有一定的参考价值。
1 在一定程度上是没问题的2 Java中的Queue是一个接口,具体的实现类有很多,比如LinkedList、ArrayDeque等。
这些实现类在向队列中添加元素时,都有相应的容量限制,当达到容量上限时就会抛出异常。
但如果频繁添加数据,可能会导致队列的长度变得很长,从而占用大量的内存空间。
3 如果需要频繁添加数据,可以考虑使用具有自动扩容功能的队列,比如PriorityQueue或ConcurrentLinkedQueue。
这些队列在添加数据时,会自动扩容以容纳更多的元素,从而避免了容量限制的问题。 参考技术A Java中的Queue是一个非常常用的数据结构,它可以用于存储和处理一系列元素,并且支持先进先出(FIFO)的访问方式。在Queue中添加和删除元素是一种基本操作,如果频繁添加数据而不及时删除,可能会导致内存占用过高的问题。
然而,在Java中,Queue的底层实现通常使用数组或链表等数据结构,可以很好地处理元素的添加和删除,并保证不会因为频繁添加数据而导致程序的错误或异常。当Queue中的元素达到一定数量时,也可以通过调整Queue的大小或使用循环队列等技术,来优化Queue的性能和内存占用情况。
需要注意的是,在多线程环境下,频繁的添加或删除操作可能会导致竞态条件或其它并发问题,因此在多线程程序中使用Queue时,需要按照线程安全的方式进行操作。例如,可以使用同步机制或线程安全的队列类(如ConcurrentLinkedQueue、BlockingQueue等)来确保程序的正确性和可靠性。
9-java安全——关于构造CC2链的几个问题
思考一下:CC2链的poc利用代码中为什么要向queue队列中添加至少2个元素?并且PriorityQueue队列中的queue属性被transient关键字修饰具有“不会序列化”语义,在反序列化过程中为什么还能从流中读取queue的数据,流中的数据又从何而来?
readObject方法是怎么从流中读取queue的数据
使用SerializationDumper工具解析对象序列化的数据,Fields字段中并没有看到PriorityQueue的queue属性,说明queue确实没有参与序列化,但是PriorityQueue重写了Serializable接口的writeObject和readObject方法实现自定义序列化与反序列化。
通过分析PriorityQueue类的writeObject方法,发现对queue属性里的内容进行了序列化。
有同学可能会有疑惑:java序列化机制不允许被transient关键字修饰的成员属性参与序列化,那为啥还是能调用writeObject方法进行序列化?
这句话没有错,但是有一个前提,如果使用默认的方式进行序列化(实现Serializable接口但不重写writeObject和readObject方法),transient关键字修饰的成员属性确实不会参与序列化,并且成员属性的内容也不会序列化。但是重写了writeObject和readObject方法就可以实现自定义序列化,调用writeObject方法并传入queue属性,这样的话queue中的内容依然会参与序列化。
那问题来了,queue属性里的内容到底写到了哪里呢?继续跟进分析writeObject方法做了什么事情?
可以看到writeObject方法会传入一个TemplatesImpl对象的参数
然后调用了writeObject0方法,经过一些列调用,会调用writeOrdinaryObject方法,该方法内部bout流调用了writeByte方法写入TC_OBJECT标记,其值为115,十六进制为0x73。
接着调用writeClassDesc方法,该方法内部调用了writeNonProxyDesc方法写入类描述信息,也就是写入TC_CLASSDESC标记,其值为114,十六进制为0x72
接着调用了writeNonProxy方法开始写入对象的信息:
void writeNonProxy(ObjectOutputStream out) throws IOException {
//写入当前对象所属类名
out.writeUTF(name);
//写入suid
out.writeLong(getSerialVersionUID());
byte flags = 0;
if (externalizable) {
flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
int protocol = out.getProtocolVersion();
if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
flags |= ObjectStreamConstants.SC_BLOCK_DATA;
}
} else if (serializable) {
flags |= ObjectStreamConstants.SC_SERIALIZABLE;
}
if (hasWriteObjectData) {
flags |= ObjectStreamConstants.SC_WRITE_METHOD;
}
if (isEnum) {
flags |= ObjectStreamConstants.SC_ENUM;
}
out.writeByte(flags);
//写入属性字段个数
out.writeShort(fields.length);
//写入字段信息
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i];
out.writeByte(f.getTypeCode());
out.writeUTF(f.getName());
if (!f.isPrimitive()) {
out.writeTypeString(f.getTypeString());
}
}
}
写入的类名为com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,写入的suid为0x09 57 4f c1 6e ac ab 33,接着依次写入对象的成员属性信息。
queue数组中第一个元素写入完毕,接着writeObject方法写入第二个元素,由于这两个元素都属于同一对象,第二次写入的时候并不会再次写入该对象的内容,而是会写入该对象的newHandle(对象引用),TemplatesImpl对象的newHandle值就是0x007e0012,TC_REFERENCE标记表示这是对一个对象引用的引用。
objectAnnotation字段的位置就是queue数据序列化的数据,如下所示:
objectAnnotation
TC_BLOCKDATA - 0x77
Length - 4 - 0x04
Contents - 0x00000003
//writeObject开始写入queue中第一个元素数据
TC_OBJECT - 0x73
TC_CLASSDESC - 0x72
className
Length - 58 - 0x00 3a
Value - com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl - 0x636f6d2e73756e2e6f72672e6170616368652e78616c616e2e696e7465726e616c2e78736c74632e747261782e54656d706c61746573496d706c
serialVersionUID - 0x09 57 4f c1 6e ac ab 33
newHandle 0x00 7e 00 0e
classDescFlags - 0x03 - SC_WRITE_METHOD | SC_SERIALIZABLE
fieldCount - 9 - 0x00 09
Fields
0:
Int - I - 0x49
fieldName
Length - 13 - 0x00 0d
Value - _indentNumber - 0x5f696e64656e744e756d626572
1:
Int - I - 0x49
fieldName
Length - 14 - 0x00 0e
Value - _transletIndex - 0x5f7472616e736c6574496e646578
2:
Boolean - Z - 0x5a
fieldName
Length - 21 - 0x00 15
Value - _useServicesMechanism - 0x5f75736553657276696365734d656368616e69736d
3:
Object - L - 0x4c
fieldName
Length - 25 - 0x00 19
Value - _accessExternalStylesheet - 0x5f61636365737345787465726e616c5374796c657368656574
className1
TC_REFERENCE - 0x71
Handle - 8257546 - 0x00 7e 00 0a
4:
Object - L - 0x4c
fieldName
Length - 11 - 0x00 0b
Value - _auxClasses - 0x5f617578436c6173736573
className1
TC_STRING - 0x74
newHandle 0x00 7e 00 0f
Length - 59 - 0x00 3b
Value - Lcom/sun/org/apache/xalan/internal/xsltc/runtime/Hashtable; - 0x4c636f6d2f73756e2f6f72672f6170616368652f78616c616e2f696e7465726e616c2f78736c74632f72756e74696d652f486173687461626c653b
5:
Array - [ - 0x5b
fieldName
Length - 10 - 0x00 0a
Value - _bytecodes - 0x5f62797465636f646573
className1
TC_STRING - 0x74
newHandle 0x00 7e 00 10
Length - 3 - 0x00 03
Value - [[B - 0x5b5b42
6:
Array - [ - 0x5b
fieldName
Length - 6 - 0x00 06
Value - _class - 0x5f636c617373
className1
TC_REFERENCE - 0x71
Handle - 8257547 - 0x00 7e 00 0b
7:
Object - L - 0x4c
fieldName
Length - 5 - 0x00 05
Value - _name - 0x5f6e616d65
className1
TC_REFERENCE - 0x71
Handle - 8257546 - 0x00 7e 00 0a
8:
Object - L - 0x4c
fieldName
Length - 17 - 0x00 11
Value - _outputProperties - 0x5f6f757470757450726f70657274696573
className1
TC_STRING - 0x74
newHandle 0x00 7e 00 11
Length - 22 - 0x00 16
Value - Ljava/util/Properties; - 0x4c6a6176612f7574696c2f50726f706572746965733b
classAnnotations
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
//TemplatesImpl对象的引用
newHandle 0x00 7e 00 12
classdata
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
values
_indentNumber
(int)0 - 0x00 00 00 00
_transletIndex
(int)-1 - 0xff ff ff ff
_useServicesMechanism
(boolean)false - 0x00
_accessExternalStylesheet
(object)
TC_STRING - 0x74
newHandle 0x00 7e 00 13
Length - 3 - 0x00 03
Value - all - 0x616c6c
_auxClasses
(object)
TC_NULL - 0x70
_bytecodes
(array)
TC_ARRAY - 0x75
TC_CLASSDESC - 0x72
className
Length - 3 - 0x00 03
Value - [[B - 0x5b5b42
serialVersionUID - 0x4b fd 19 15 67 67 db 37
newHandle 0x00 7e 00 14
classDescFlags - 0x02 - SC_SERIALIZABLE
fieldCount - 0 - 0x00 00
classAnnotations
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
newHandle 0x00 7e 00 15
Array size - 1 - 0x00 00 00 01
Values
Index 0:
(array)
TC_ARRAY - 0x75
TC_CLASSDESC - 0x72
className
Length - 2 - 0x00 02
Value - [B - 0x5b42
serialVersionUID - 0xac f3 17 f8 06 08 54 e0
newHandle 0x00 7e 00 16
classDescFlags - 0x02 - SC_SERIALIZABLE
fieldCount - 0 - 0x00 00
classAnnotations
TC_ENDBLOCKDATA - 0x78
superClassDesc
TC_NULL - 0x70
newHandle 0x00 7e 00 17
Array size - 1455 - 0x00 00 05 af
Values
//省略......
_class
(array)
TC_NULL - 0x70
_name
(object)
TC_STRING - 0x74
newHandle 0x00 7e 00 18
Length - 13 - 0x00 0d
Value - TemplatesImpl - 0x54656d706c61746573496d706c
_outputProperties
(object)
TC_NULL - 0x70
objectAnnotation
TC_BLOCKDATA - 0x77
Length - 1 - 0x01
Contents - 0x00
TC_ENDBLOCKDATA - 0x78
//写入queue中第二个元素
TC_REFERENCE - 0x71
Handle - 8257554 - 0x00 7e 00 12
TC_ENDBLOCKDATA - 0x78
在java序列化规范中,从objectAnnotation表示写入序列化时自定义的数据,因此queue中的元素都会写入到objectAnnotation字段开始的位置。第二次写入queue数组的元素并不会直接写入数据,而是直接写入TemplatesImpl对象的引用,
TC_REFERENCE标记表示(Handle - 8257554 - 0x00 7e 00 12)这段数据是一个序列化对象的引用,Handle表示该序列化对象的引用(TemplatesImpl对象的newHandle引用)。
通过分析writeObject方法我们知道queue队列中的数据仍然可以参与自定义序列化与反序列化,需要注意的是必须自己定义和控制数据解析的规则,说白了就是你怎么序列化的,就必须怎么反序列化,如果不遵守规范数据就会出错。
关于queue队列中为什么要添加2个元素的问题?原因在于,queue队列在反序列化过程中readObject方法中size属性会记录队列中元素的个数,接着调用了一个heapify方法,且该方法内部的for循环会判断size的个数,如果size值小于2的话不会调用siftDown方法,也不会调用comparator比较器的compare方法进行排序,最终不会触发之前构造的利用链。
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
以上是关于java中频繁的queue里添加数据没问题吧的主要内容,如果未能解决你的问题,请参考以下文章