2009-02-20 34 views
16

Tôi đang gặp sự cố khi sắp xếp nhiều đối tượng trong .NET. Biểu đồ đối tượng khá lớn với một số tập dữ liệu mới đang được sử dụng, vì vậy tôi nhận được:SerializationException khi serializing nhiều đối tượng trong .NET

System.Runtime.Serialization.SerializationException 
"The internal array cannot expand to greater than Int32.MaxValue elements." 

Có ai khác đã đạt đến giới hạn này không? Bạn đã giải quyết nó như thế nào?

Sẽ tốt hơn nếu tôi vẫn còn có thể sử dụng được xây dựng trong cơ chế serialization nếu có thể, nhưng nó có vẻ như phải chỉ cuộn của riêng tôi (và duy trì tính tương thích ngược với các tập tin dữ liệu hiện có)

Đối tượng là tất cả POCO và đang được tuần tự hóa sử dụng BinaryFormatter. Mỗi đối tượng được serialized thực hiện ISerializable để chọn lọc serialize các thành viên của nó (một số người trong số họ được tính toán lại trong quá trình tải).

Dường như đây là sự cố mở đối với MS (details here), nhưng nó đã được giải quyết là Wont Fix. Chi tiết là (từ liên kết):

Nối tiếp nhị phân không thành đối tượng đồ thị với hơn ~ 13.2 triệu đối tượng. Nỗ lực làm như vậy gây ra một ngoại lệ trong ObjectIDGenerator.Rehash với thông báo lỗi gây hiểu lầm tham chiếu Int32.MaxValue.

Sau khi kiểm tra ObjectIDGenerator.cs trong mã nguồn SSCLI , dường như lớn hơn đồ thị đối tượng có thể được xử lý bởi thêm các mục bổ sung vào mảng kích cỡ. Xem những dòng sau:

// Table of prime numbers to use as hash table sizes. Each entry is the 
// smallest prime number larger than twice the previous entry. 
private static readonly int[] sizes = {5, 11, 29, 47, 97, 197, 397, 
797, 1597, 3203, 6421, 12853, 25717, 51437, 102877, 205759, 
411527, 823117, 1646237, 3292489, 6584983}; 

Tuy nhiên, nó sẽ được tốt đẹp nếu serialization làm việc cho bất kỳ kích thước hợp lý của đồ thị đối tượng.

Trả lời

9

Tôi đã cố gắng tạo lại sự cố, nhưng mã chỉ mất một thời gian để chạy ngay cả khi mỗi đối tượng trong số hơn 13 triệu chỉ là 2 byte. Vì vậy, tôi nghi ngờ bạn không chỉ có thể khắc phục vấn đề, nhưng cũng cải thiện đáng kể hiệu suất nếu bạn đóng gói dữ liệu của bạn tốt hơn một chút trong triển khai ISerialize tùy chỉnh của bạn. Đừng để serializer nhìn sâu vào cấu trúc của bạn, nhưng cắt nó ở điểm mà đồ thị đối tượng của bạn thổi lên hàng trăm nghìn phần tử mảng hoặc nhiều hơn (vì có lẽ nếu bạn có nhiều đối tượng, chúng khá nhỏ hoặc bạn sẽ không thể giữ chúng trong bộ nhớ anyway). Lấy ví dụ này, cho phép trình nối tiếp xem các lớp B và C, nhưng quản lý thủ công bộ sưu tập lớp A:

class Program 
{ 
    static void Main(string[] args) 
    { 
     C c = new C(8, 2000000); 
     System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
     System.IO.MemoryStream ms = new System.IO.MemoryStream(); 
     bf.Serialize(ms, c); 
     ms.Seek(0, System.IO.SeekOrigin.Begin); 
     for (int i = 0; i < 3; i++) 
      for (int j = i; j < i + 3; j++) 
       Console.WriteLine("{0}, {1}", c.all[i][j].b1, c.all[i][j].b2); 
     Console.WriteLine("====="); 
     c = null; 
     c = (C)(bf.Deserialize(ms)); 
     for (int i = 0; i < 3; i++) 
      for (int j = i; j < i + 3; j++) 
       Console.WriteLine("{0}, {1}", c.all[i][j].b1, c.all[i][j].b2); 
     Console.WriteLine("====="); 
    } 
} 

class A 
{ 
    byte dataByte1; 
    byte dataByte2; 
    public A(byte b1, byte b2) 
    { 
     dataByte1 = b1; 
     dataByte2 = b2; 
    } 

    public UInt16 GetAllData() 
    { 
     return (UInt16)((dataByte1 << 8) | dataByte2); 
    } 

    public A(UInt16 allData) 
    { 
     dataByte1 = (byte)(allData >> 8); 
     dataByte2 = (byte)(allData & 0xff); 
    } 

    public byte b1 
    { 
     get 
     { 
      return dataByte1; 
     } 
    } 

    public byte b2 
    { 
     get 
     { 
      return dataByte2; 
     } 
    } 
} 

[Serializable()] 
class B : System.Runtime.Serialization.ISerializable 
{ 
    string name; 
    List<A> myList; 

    public B(int size) 
    { 
     myList = new List<A>(size); 

     for (int i = 0; i < size; i++) 
     { 
      myList.Add(new A((byte)(i % 255), (byte)((i + 1) % 255))); 
     } 
     name = "List of " + size.ToString(); 
    } 

    public A this[int index] 
    { 
     get 
     { 
      return myList[index]; 
     } 
    } 

    #region ISerializable Members 

    public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) 
    { 
     UInt16[] packed = new UInt16[myList.Count]; 
     info.AddValue("name", name); 
     for (int i = 0; i < myList.Count; i++) 
     { 
      packed[i] = myList[i].GetAllData(); 
     } 
     info.AddValue("packedData", packed); 
    } 

    protected B(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) 
    { 
     name = info.GetString("name"); 
     UInt16[] packed = (UInt16[])(info.GetValue("packedData", typeof(UInt16[]))); 
     myList = new List<A>(packed.Length); 
     for (int i = 0; i < packed.Length; i++) 
      myList.Add(new A(packed[i])); 
    } 

    #endregion 
} 

[Serializable()] 
class C 
{ 
    public List<B> all; 
    public C(int count, int size) 
    { 
     all = new List<B>(count); 
     for (int i = 0; i < count; i++) 
     { 
      all.Add(new B(size)); 
     } 
    } 
} 
+0

Đóng gói dữ liệu giống như một ý tưởng rất hay. Tôi thậm chí có thể sử dụng MemoryStream để thực hiện việc đóng gói - vì vậy rất nhiều mã sẽ không cần thay đổi (nó chỉ có thể tiếp tục lưu cách hiện tại). Và có thể chỉ dành cho các lớp 'phổ biến' để có được số lượng đối tượng được lưu vào một số hợp lý. – Wilka

0

Tôi đoán ... sắp xếp từng đối tượng ít hơn tại một thời điểm?

2 câu hỏi chính:

  • những gì đối tượng chúng là gì?
    • POCO?
    • DataTable?
  • loại serialization là gì?
    • xml?
      • XmlSerializer?
      • DataContractSerializer?
    • nhị phân?
      • BinaryFormatter?
      • SoapFormatter?
    • khác?
      • json?
      • bespoke?

serialization cần phải có một số xem xét những gì mà khối lượng dữ liệu là; ví dụ, một số khung tuần tự hóa hỗ trợ phát trực tuyến cả hai đối tượng và dữ liệu được tuần tự hóa, thay vì dựa vào biểu đồ đối tượng hoàn chỉnh hoặc bộ nhớ tạm thời.

Một tùy chọn khác là tuần tự hóa các tập hợp dữ liệu đồng nhất thay vì đồ thị đầy đủ - tức là sắp xếp theo thứ tự tất cả các "khách hàng" riêng biệt "các đơn đặt hàng"; điều này thường sẽ làm giảm khối lượng, với chi phí phức tạp hơn.

Vì vậy: kịch bản ở đây là gì?

+0

Tôi đã cập nhật câu hỏi (hy vọng) bao gồm 2 câu hỏi của bạn. – Wilka

0

Có vẻ như bạn đã gặp phải một hạn chế nội bộ trong khung công tác. Bạn có thể viết serialization của riêng bạn bằng cách sử dụng BinaryReader/Writer hoặc DataContractSerializer hoặc bất cứ điều gì, nhưng nó không phải là lý tưởng tôi biết.

0

Dude, bạn đã đến cuối .net!

tôi đã không đạt giới hạn này, nhưng đây là một vài gợi ý:

  1. sử dụng [XmlIgnore] để bỏ qua một số đối tượng - có thể bạn không cần phải serialize tất cả mọi thứ

  2. bạn có thể sử dụng serializer bằng tay (tức là không có thuộc tính, nhưng bằng cách thực hiện Serialize()) và phân vùng các mô hình thành nhiều tệp hơn.

+0

XmlIgnore chỉ hoạt động cho việc tuần tự hóa XML. –

1

Bạn đã nghĩ về thực tế là Int32.MaxValue là 2,147,483,647 - hơn 2 tỷ .

Bạn cần 16GB bộ nhớ chỉ để lưu trữ các con trỏ (giả định máy 64 bit), hãy để mình một mình đối tượng. Một nửa trên máy 32 bit, mặc dù việc nén 8 GB dữ liệu con trỏ vào tối đa 3GB hoặc không gian có thể sử dụng sẽ là một mẹo tốt.

Tôi mạnh mẽ nghi ngờ rằng vấn đề của bạn không phải là số đối tượng, nhưng khung tuần tự hóa đang đi vào một loại vòng lặp vô hạn bởi vì bạn có vòng tham chiếu trong cấu trúc dữ liệu của mình.

xem xét lớp đơn giản này:

public class Node 
{ 
    public string Name {get; set;} 
    public IList<Node> Children {get;} 
    public Node Parent {get; set;} 
    ... 
} 

lớp đơn giản này không thể được đăng, bởi vì sự hiện diện của các tài sản có nghĩa là Chánh serialization sẽ đi vào một vòng lặp vô hạn.

Vì bạn đã triển khai ISerializable, bạn là 75% cách để giải quyết vấn đề này - bạn chỉ cần đảm bảo bạn xóa mọi chu kỳ khỏi biểu đồ đối tượng bạn đang lưu trữ, để lưu trữ đối tượng cây thay thế.

Một kỹ thuật thường được sử dụng là để lưu trữ các tên (hoặc id) của một đối tượng tham chiếu thay vì các tài liệu tham khảo thực tế, giải quyết tên lại cho các đối tượng trên tải.

+2

Câu hỏi nói BinaryFormatter; xử lý tham chiếu/đệ quy một cách chính xác. –

+1

Thông báo Int32.MaxValue gây hiểu lầm, tôi sẽ thêm một chút chi tiết nữa vào câu hỏi của tôi để mở rộng. – Wilka

0

Bạn có cần tìm nạp tất cả dữ liệu cùng một lúc không? Mười ba triệu đối tượng là rất nhiều thông tin để xử lý cùng một lúc.

Bạn có thể triển khai cơ chế phân trang và tìm nạp dữ liệu theo các đoạn nhỏ hơn. Và nó có thể làm tăng sự đáp ứng của ứng dụng, vì bạn sẽ không phải chờ đợi cho tất cả các đối tượng đó kết thúc tuần tự hóa.

+0

Nó cần hầu hết các dữ liệu cùng một lúc (một số có thể có thể được cạo bỏ). Nó cần thiết cho việc phân tích thống kê. Tôi không quan tâm đến việc sử dụng bộ nhớ của phần này của chương trình (nó đang chạy trên 64bit với một số tiền phong nha của ram). Trao đổi những thứ ra đĩa sẽ làm cho phân tích rất chậm. – Wilka

1

Tùy thuộc vào cấu trúc của dữ liệu, có thể bạn có thể tuần tự hóa/deserialize đồ thị con của đồ thị đối tượng lớn của bạn? Nếu dữ liệu có thể được phân chia bằng cách nào đó, bạn có thể thoát khỏi nó, chỉ tạo ra sự sao chép nhỏ dữ liệu tuần tự hóa.

Các vấn đề liên quan