Đối với Java, việc nhóm các đối tượng * không hữu ích lắm vì chu trình GC đầu tiên cho các đối tượng vẫn ở xung quanh sẽ định hình lại chúng trong bộ nhớ, di chuyển chúng ra khỏi không gian "Eden" và có khả năng mất địa phương trong quá trình.
- Nó luôn hữu ích trong bất kỳ ngôn ngữ nào để tập hợp các tài nguyên phức tạp rất tốn kém để phá hủy và tạo ra như các luồng. Chúng có thể có giá trị gộp vì chi phí tạo và hủy chúng gần như không liên quan gì đến bộ nhớ liên quan đến xử lý đối tượng với tài nguyên. Tuy nhiên, các hạt không phù hợp với thể loại này.
Java cung cấp phân bổ cụm nhanh bằng cách sử dụng bộ cấp phát tuần tự khi bạn phân bổ nhanh các đối tượng vào không gian Eden. Chiến lược phân bổ tuần tự đó là siêu nhanh, nhanh hơnmalloc
C vì nó chỉ tập hợp bộ nhớ đã được phân bổ theo kiểu tuần tự thẳng, nhưng nó đi kèm với nhược điểm là bạn không thể giải phóng từng phần bộ nhớ. Đây cũng là một mẹo hữu ích trong C nếu bạn chỉ muốn phân bổ mọi thứ cực nhanh cho một cấu trúc dữ liệu mà bạn không cần phải xóa bất cứ thứ gì khỏi nó, chỉ cần thêm mọi thứ và sau đó sử dụng nó và ném toàn bộ mọi thứ sau đó.
Do nhược điểm này là không thể giải phóng các đối tượng riêng lẻ, Java GC, sau một chu kỳ đầu tiên, sẽ sao chép tất cả bộ nhớ được phân bổ từ không gian Eden sang các vùng bộ nhớ mới bằng cách sử dụng bộ cấp phát bộ nhớ đa năng, chậm hơn, cho phép bộ nhớ được giải phóng trong các khối riêng lẻ trong một chủ đề khác nhau. Sau đó, nó có thể loại bỏ toàn bộ bộ nhớ được phân bổ trong không gian Eden mà không cần bận tâm đến các đối tượng riêng lẻ đã được sao chép và sống ở nơi khác trong bộ nhớ. Sau chu trình GC đầu tiên đó, các đối tượng của bạn có thể bị phân mảnh trong bộ nhớ.
Do các đối tượng cuối cùng có thể bị phân mảnh sau chu kỳ GC đầu tiên đó, nên lợi ích của việc tập hợp đối tượng khi chủ yếu là để cải thiện các mẫu truy cập bộ nhớ (địa phương tham chiếu) và giảm chi phí phân bổ / phân bổ phần lớn bị mất ... rất nhiều rằng bạn sẽ có được địa phương tham chiếu tốt hơn thông thường bằng cách chỉ phân bổ các hạt mới mọi lúc và sử dụng chúng trong khi chúng vẫn còn mới trong không gian Eden và trước khi chúng trở nên "cũ" và có khả năng bị phân tán trong bộ nhớ. Tuy nhiên, điều có thể cực kỳ hữu ích (như có được đối thủ hiệu năng C trong Java) là tránh sử dụng các đối tượng cho các hạt của bạn và gộp dữ liệu nguyên thủy cũ. Ví dụ đơn giản, thay vì:
class Particle
{
public float x;
public float y;
public boolean alive;
}
Làm một cái gì đó như:
class Particles
{
// X positions of all particles. Resize on demand using
// 'java.util.Arrays.copyOf'. We do not use an ArrayList
// since we want to work directly with contiguously arranged
// primitive types for optimal memory access patterns instead
// of objects managed by GC.
public float x[];
// Y positions of all particles.
public float y[];
// Alive/dead status of all particles.
public bool alive[];
}
Bây giờ để sử dụng lại bộ nhớ cho các hạt hiện có, bạn có thể làm điều này:
class Particles
{
// X positions of all particles.
public float x[];
// Y positions of all particles.
public float y[];
// Alive/dead status of all particles.
public bool alive[];
// Next free position of all particles.
public int next_free[];
// Index to first free particle available to reclaim
// for insertion. A value of -1 means the list is empty.
public int first_free;
}
Bây giờ khi nth
hạt chết, để cho phép nó được tái sử dụng, hãy đẩy nó vào danh sách miễn phí như vậy:
alive[n] = false;
next_free[n] = first_free;
first_free = n;
Khi thêm một hạt mới, hãy xem bạn có thể bật một chỉ mục từ danh sách miễn phí không:
if (first_free != -1)
{
int index = first_free;
// Pop the particle from the free list.
first_free = next_free[first_free];
// Overwrite the particle data:
x[index] = px;
y[index] = py;
alive[index] = true;
next_free[index] = -1;
}
else
{
// If there are no particles in the free list
// to overwrite, add new particle data to the arrays,
// resizing them if needed.
}
Đây không phải là mã dễ chịu nhất để làm việc, nhưng với điều này, bạn sẽ có thể có được một số mô phỏng hạt rất nhanh với xử lý hạt tuần tự luôn rất thân thiện với bộ đệm vì tất cả dữ liệu hạt sẽ luôn được lưu trữ liên tục. Loại đại diện SoA này cũng làm giảm việc sử dụng bộ nhớ vì chúng ta không phải lo lắng về việc đệm, siêu dữ liệu đối tượng cho phản xạ / công văn động và nó phân tách các trường nóng khỏi các trường lạnh (ví dụ: chúng ta không nhất thiết phải quan tâm đến dữ liệu các trường như màu của hạt trong quá trình vật lý đi qua, thật lãng phí khi chỉ tải nó vào một dòng bộ đệm để không sử dụng nó và đuổi nó đi).
Để làm cho mã dễ làm việc hơn, có thể đáng để viết các thùng chứa có thể thay đổi kích thước cơ bản của riêng bạn lưu trữ các mảng float, mảng số nguyên và mảng booleans. Một lần nữa, bạn không thể sử dụng thuốc generic và ArrayList
ở đây (ít nhất là từ lần cuối cùng tôi kiểm tra) vì điều đó đòi hỏi các đối tượng được quản lý bởi GC, không phải dữ liệu nguyên thủy liền kề. Chúng tôi muốn sử dụng mảng liền kề int
, ví dụ, không phải mảng được quản lý bởi GC Integer
không nhất thiết phải liền kề sau khi rời khỏi không gian Eden.
Với các mảng kiểu nguyên thủy, chúng luôn được đảm bảo liền kề nhau và do đó bạn có được địa phương tham chiếu cực kỳ mong muốn (để xử lý hạt tuần tự, nó tạo ra một thế giới khác biệt) và tất cả các lợi ích mà nhóm đối tượng dự định cung cấp. Với một mảng các đối tượng, thay vào đó nó hơi giống với một loạt các con trỏ bắt đầu chỉ vào các đối tượng theo kiểu liền kề giả định rằng bạn đã phân bổ tất cả chúng cùng một lúc vào không gian Eden, nhưng sau một chu trình GC, có thể được chỉ ra khắp nơi trong ký ức.