2015-01-11 17 views
15

Tôi đang cố gắng tìm hiểu làm thế nào để PHP nạp mảng vào bộ nhớ và khi nào vượt qua một mảng tiêu thụ bộ nhớ.Quản lý sử dụng bộ nhớ mảng của PHP hoạt động như thế nào?

Vì vậy, tôi đã có chút này chút mã chạy: lưu ý rằng các mảng đầu vào là ít quan trọng trong ví dụ này:

<?php 

echo $this->getMemoryUsage(); 
$arr = $query->result_array(); // array of arrays from codeigniter 
echo $this->getMemoryUsage(); 

này tiêu thụ chính xác 250 kB bộ nhớ, điều này có nghĩa mảng là khoảng 250 kB có kích thước, khoảng.

Vì vậy, tôi chạy đoạn mã sau:

<?php 

echo $this->getMemoryUsage(); 
$arr = $query->result_array(); // array of arrays from codeigniter 

$arr[0]['id'] = 'changing this value'; 

$foo = $arr; 
$foo[2]['id'] = 'changing this value again'; 

$bar = $foo; 
$bar[4]['id'] = 'changing this value again and again'; 

$far = $bar; 
$far[5]['id'] = 'changing this value again and again and again'; 

echo $this->getMemoryUsage(); 

Theo những gì tôi đọc và được cho biết, PHP không thực sự sao chép mảng, nó chỉ tham chiếu mảng ban đầu, nhưng một lần thay đổi là làm cho PHP phải sao chép toàn bộ mảng.

Hãy tưởng tượng sự ngạc nhiên của tôi khi mã trên tiêu thụ chính xác 500 kB RAM.

Có ai có thể giải thích những gì đang xảy ra ở đây không?

Chỉ cần rõ ràng, tất cả các chỉ mục này (0-5 và id) đã tồn tại trong mảng ban đầu, tôi chỉ sửa đổi giá trị. Giá trị ban đầu là một số nguyên.

EDIT

Chỉ cần để xóa sự tham gia của $ this-> kết quả(); Dưới đây là một thử nghiệm tôi đã thực hiện:

echo $this->getMemoryUsage(); 
    $arr = $query->result_array(); // array of arrays from codeigniter 
//$arr[0]['id'] = 'changing this value'; 

    $foo = $arr; 
    $foo[2]['id'] = 'changing this value again'; 

    //$bar = $foo; 
    //$bar[4]['id'] = 'changing this value again and again'; 
    // 
    //$far = $bar; 
    //$far[4]['id'] = 'changing this value again and again and again'; 

    echo $this->getMemoryUsage(); 

Lần này đầu ra là chính xác 250 kB - Cũng giống như các thử nghiệm ban đầu mà không cần bất kỳ thay đổi

EDIT # 2

Theo yêu cầu, tôi đã chạy mã từ đây trên thiết lập của tôi, để đảm bảo kết quả nhất quán: http://pastebin.com/cYNg4cg7

Đây là kết quả:

KÊ KHAI: 4608 kB
CUỐI CÙNG: 8904 kB
DIFF ĐỂ KÊ KHAI: 4296 kB

Vì vậy, mặc dù tuyên bố là 4608 và mảng đã được thông qua và thay đổi 4 lần, nó vẫn chỉ ít hơn gấp đôi bộ nhớ dấu chân.

EDIT # 3

Tôi đã chạy những thay đổi bộ nhớ sau mỗi lần phân bổ:

KÊ KHAI: 5144 kB
phân bổ A0 thêm: 144 kB
phân bổ A1 thêm: 1768 kB
phân bổ A2 được thêm vào: 1768 kB
phân bổ thêm A3: 1768 kB
CUỐI CÙNG: 10744 kB
DIFF TO DECLARATION: 5600 kB

Mỗi hoạt động sau đây sau khi chi phí đầu tiên giống hệt nhau, có vẻ như cho biết cùng một kích thước chính xác đang được sao chép. Điều này dường như để hỗ trợ câu trả lời của Austin, Điều duy nhất mà không thêm lên bây giờ là kích thước được phân bổ, Nhưng đó là một câu hỏi khác nhau.

Có vẻ như Austin đang chơi bóng, tôi sẽ chấp nhận nếu không có câu trả lời nào khác.

+0

câu hỏi rất khó khăn, bạn có thể quan tâm đến các bài viết sau đây tôi đọc một vài ngày trước: https: // nikic .github.io/2011/12/12/Làm thế nào lớn-là-PHP-mảng-thực sự-Hint-BIG.html – Fleshgrinder

+0

Tôi đã đọc bài viết đó một vài tuần trước đây, It's honestly fasci nating, Nhưng không giải thích cách sao chép hoạt động chính xác. – Patrick

+2

Tôi biết, chỉ nghĩ rằng bạn có thể thích nó. Tôi không thể trả lời câu hỏi của bạn và tôi không thể cung cấp cho bạn liên kết có thể trả lời câu hỏi của bạn. Thay vào đó tôi đánh dấu sao câu hỏi của bạn để tôi có thể theo dõi nó và sẽ cho nó một tiền thưởng nếu không có câu trả lời được đăng, vì tôi cũng muốn biết điều này. :) – Fleshgrinder

Trả lời

4

Dưới đây là những gì tôi nghĩ đang xảy ra:

PHP mảng là bản sao trên ghi như bạn nói, nhưng mỗi cấp độ của một mảng đa chiều được sao chép riêng rẽ trên ghi. PHP rất thông minh về việc tái sử dụng các phần của mảng đa chiều chứ không chỉ toàn bộ. (Điều này cũng tương tự như một số hệ thống tập tin có hỗ trợ ảnh chụp nhanh, như ZFS.)

Ví dụ: nói rằng chúng ta có mảng này

$x = array('foo' => array(1, 2, 3), 'bar' => array(4, 5, 6)); 

này được lưu trữ trong bộ nhớ không phải là một đoạn duy nhất, nhưng khi khối riêng biệt ở đây nhãn A, B, C, và $x:

array(1, 2, 3) //A 
array(4, 5, 6) //B 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
{pointer to C} //$x 

Bây giờ cho phép tạo một bản sao của $x:

$y = $x; 

này sử dụng rất ít bộ nhớ thêm, bởi vì tất cả nó phải làm là tạo ra một con trỏ đến C:

array(1, 2, 3) //A 
array(4, 5, 6) //B 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
{pointer to C} //$x 
{pointer to C} //$y 

Bây giờ cho phép thay đổi $y:

$y['foo'][0] = 10; 

Đây là những gì không xảy ra :

array(1, 2, 3) //A 
array(10, 2, 3) //A2 
array(4, 5, 6) //B 
array(4, 5, 6) //B2 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
array('foo' => {pointer to A2}, 'bar' => {pointer to B2}) //C2 
{pointer to C} //$x 
{pointer to C2} //$y 

Lưu ý rằng BB2 giống hệt nhau. Không cần để giữ những điều tương tự hai lần, vì vậy những gì thực sự xảy ra này là:

array(1, 2, 3) //A 
array(10, 2, 3) //A2 
array(4, 5, 6) //B 
array('foo' => {pointer to A}, 'bar' => {pointer to B}) //C 
array('foo' => {pointer to A2}, 'bar' => {pointer to B}) //C2 
{pointer to C} //$x 
{pointer to C2} //$y 

Trong trường hợp đơn giản này, lợi ích là khá nhỏ, nhưng hãy tưởng tượng rằng thay vì ba con số, các mảng 'bar' chứa hàng ngàn số . Bạn sẽ tiết kiệm được rất nhiều bộ nhớ.

Liên kết mã này với mã ban đầu của bạn, thử in ra việc sử dụng bộ nhớ không chỉ ở đầu và cuối, mà sau mỗi lần gán mảng mới.Bạn sẽ thấy rằng việc sử dụng bộ nhớ chỉ tăng một phần của những gì mảng ban đầu chiếm sau mỗi bước. Điều này là do chỉ một phần của mảng đang được sao chép chứ không phải toàn bộ. Cụ thể, mảng cấp đầu tiên và mảng phụ cụ thể mà bạn thay đổi được sao chép, nhưng các mảng phụ khác không được sao chép.

Thực tế là số tiền cuối cùng của bộ nhớ được sử dụng gấp đôi số tiền bắt đầu có vẻ trùng hợp do thiết lập mã của bạn và số bản sao của mảng bạn tạo.

(Trong thực tế, PHP có thể làm tốt hơn những gì tôi mô tả ở đây (có thể sẽ chỉ giữ một bản sao 'foo''bar', v.v.), nhưng phần lớn nó lại có cùng một mẹo.)

Nếu bạn muốn có một cuộc biểu tình ấn tượng hơn điều này, làm một cái gì đó như thế này:

$base = memory_get_usage(); 
$x = array('small' => array('this is small'), 'big' => array()); 
for ($i = 0; $i < 1000000; $i++) { 
    $x['big'][] = $i; 
} 
echo (memory_get_usage() - $base).PHP_EOL; //a lot of memory 
$y = $x; 
$y['small'][0] = 'now a bit bigger'; 
echo (memory_get_usage() - $base).PHP_EOL; //a bit more memory 
$z = $x; 
$z['big'][0] = 2; 
echo (memory_get_usage() - $base).PHP_EOL; //a LOT more memory 
+0

Hey, tôi đã nghĩ gì đó dọc theo những dòng này, Tuy nhiên nó không thêm lên, Sử dụng một trường hợp thử nghiệm khác, nó đã được sao chép khi phân bổ bộ nhớ tăng gấp đôi sau 4 hành động giống hệt nhau (xem chỉnh sửa cuối cùng trong câu hỏi). 4 hành động giống nhau trên các mảng có cùng kích thước phải có hiệu ứng tuyến tính. Nếu chúng tôi thực hiện chính xác cùng một hành động 4 lần, một lần duy nhất sẽ tốn 1/4 chi phí. Lưu ý rằng trong các bài kiểm tra tôi tiếp tục thay đổi các chỉ số khác nhau, Nếu chúng ta sao chép một chỉ mục, chúng ta sẽ sao chép tất cả. Tôi sẽ chỉnh sửa câu hỏi của mình với các giá trị bộ nhớ mới ngay khi có thể. – Patrick

+2

@Patrick Hành động đầu tiên không tốn kém nhiều, bởi vì bạn không phải giữ giá trị cũ vì không có biến nào sử dụng nó. Hành động thứ hai đến thứ tư phải tạo một bản sao vì bản gốc vẫn đang được sử dụng.Sử dụng bộ nhớ in sau mỗi lần gán để xem điều này. – Austin

+0

Sau khi kiểm tra thêm câu trả lời của bạn thực sự là chính xác, tuyệt vời! :) – Patrick

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