Làm thế nào để xác định vùng nhớ (kích thước) của một biến?


102

Có một hàm nào đó trong PHP (hoặc một phần mở rộng PHP) để tìm ra một biến nhất định sử dụng bao nhiêu bộ nhớ không? sizeofchỉ cho tôi biết số phần tử / thuộc tính.

memory_get_usagegiúp ở chỗ nó cung cấp cho tôi kích thước bộ nhớ được sử dụng bởi toàn bộ tập lệnh. Có cách nào để làm điều này cho một biến duy nhất không?

Lưu ý rằng điều này là trên máy phát triển, vì vậy việc tải các tiện ích mở rộng hoặc công cụ gỡ lỗi là khả thi.


Đã chỉnh sửa - đã 5 năm sau và một số vấn đề vẫn chưa được giải quyết :(
Piskvor rời tòa nhà

Câu trả lời:


46

Bạn có thể cần một Hồ sơ bộ nhớ. Tôi đã thu thập thông tin từ SO nhưng tôi đã sao chép một số điều quan trọng có thể giúp bạn.

Như bạn có thể biết, Xdebug đã bỏ hỗ trợ cấu hình bộ nhớ kể từ phiên bản 2. *. Vui lòng tìm kiếm chuỗi "chức năng bị loại bỏ" tại đây: http://www.xdebug.org/updates.php

Đã loại bỏ các chức năng

Đã xóa hỗ trợ cho cấu hình Bộ nhớ vì nó không hoạt động bình thường.

Các tùy chọn hồ sơ khác

php-memory-profiler

https://github.com/arnaud-lb/php-memory-profiler . Đây là những gì tôi đã làm trên máy chủ Ubuntu của mình để kích hoạt nó:

sudo apt-get install libjudy-dev libjudydebian1
sudo pecl install memprof
echo "extension=memprof.so" > /etc/php5/mods-available/memprof.ini
sudo php5enmod memprof
service apache2 restart

Và sau đó trong mã của tôi:

<?php
memprof_enable();
// do your stuff
memprof_dump_callgrind(fopen("/tmp/callgrind.out", "w"));

Cuối cùng mở callgrind.outtệp bằng KCachegrind

Sử dụng gperftools của Google (được khuyến nghị!)

Trước hết, hãy cài đặt Google gperftools bằng cách tải xuống gói mới nhất tại đây: https://code.google.com/p/gperftools/

Sau đó, như mọi khi:

sudo apt-get update
sudo apt-get install libunwind-dev -y
./configure
make
make install

Bây giờ trong mã của bạn:

memprof_enable();

// do your magic

memprof_dump_pprof(fopen("/tmp/profile.heap", "w"));

Sau đó, mở thiết bị đầu cuối của bạn và khởi chạy:

pprof --web /tmp/profile.heap

pprof sẽ tạo một cửa sổ mới trong phiên trình duyệt hiện tại của bạn với một cái gì đó như hình dưới đây:

Cấu hình bộ nhớ PHP với memprof và gperftools

Xhprof + Xhgui (theo ý kiến ​​của tôi là tốt nhất để cấu hình cả cpu và bộ nhớ)

Với XhprofXhgui, bạn có thể định cấu hình việc sử dụng cpu hoặc chỉ sử dụng bộ nhớ nếu đó là vấn đề của bạn vào lúc này. Đó là một giải pháp rất hoàn chỉnh, nó cung cấp cho bạn toàn quyền kiểm soát và các bản ghi có thể được ghi cả trên mongo hoặc trong hệ thống tệp.

Để biết thêm chi tiết xem tại đây .

Lửa đen

Blackfire là một hồ sơ PHP của SensioLabs, Symfony2 guys https://blackfire.io/

Nếu bạn sử dụng Puphpet để thiết lập máy ảo của mình, bạn sẽ rất vui khi biết nó được hỗ trợ ;-)

Xdebug và truy tìm mức sử dụng bộ nhớ

XDEBUG2 là một phần mở rộng cho PHP. Xdebug cho phép bạn ghi nhật ký tất cả các lệnh gọi hàm, bao gồm các tham số và trả về giá trị cho một tệp ở các định dạng khác nhau. Có ba định dạng đầu ra. Một được coi là dấu vết con người có thể đọc được, một dấu vết khác phù hợp hơn với các chương trình máy tính vì nó dễ dàng hơn để phân tích cú pháp và dấu vết cuối cùng sử dụng HTML để định dạng dấu vết. Bạn có thể chuyển đổi giữa hai định dạng khác nhau bằng cài đặt này. Một ví dụ sẽ có sẵn ở đây

pháo đài

forp trình biên dịch PHP đơn giản, không xâm nhập, định hướng sản xuất,. Một số tính năng là:

  • đo thời gian và bộ nhớ được cấp phát cho từng chức năng

  • Sử dụng CPU

  • tệp và số dòng của lệnh gọi hàm

  • xuất ra dưới dạng định dạng Sự kiện theo dõi của Google

  • chú thích các chức năng

  • nhóm các chức năng

  • bí danh của các hàm (hữu ích cho các hàm ẩn danh)

DBG

DBG là một trình gỡ lỗi php đầy đủ tính năng, một công cụ tương tác giúp bạn gỡ lỗi các tập lệnh php. Nó hoạt động trên máy chủ WEB sản xuất và / hoặc phát triển và cho phép bạn gỡ lỗi các tập lệnh cục bộ hoặc từ xa, từ IDE hoặc bảng điều khiển và các tính năng của nó là:

  • Gỡ lỗi từ xa và cục bộ

  • Kích hoạt rõ ràng và ngầm định

  • Ngăn xếp cuộc gọi, bao gồm lệnh gọi hàm, lệnh gọi phương thức động và tĩnh, với các tham số của chúng

  • Điều hướng thông qua ngăn xếp cuộc gọi với khả năng đánh giá các biến ở các vị trí tương ứng (lồng nhau)

  • Bước vào / Bước ra / Bước qua / Chạy đến chức năng con trỏ

  • Các điểm ngắt có điều kiện

  • Điểm ngắt toàn cầu

  • Ghi nhật ký cho các lỗi và cảnh báo

  • Nhiều phiên đồng thời để gỡ lỗi song song

  • Hỗ trợ giao diện người dùng GUI và CLI

  • Hỗ trợ mạng IPv6 và IPv4

  • Tất cả dữ liệu được chuyển bởi trình gỡ lỗi có thể được bảo vệ tùy chọn bằng SSL


2
Đó chính xác là thông tin tôi đang tìm kiếm, xin cảm ơn.
Piskvor rời tòa nhà

93

Không có cách nào trực tiếp để sử dụng bộ nhớ của một biến duy nhất, nhưng như Gordon đã đề xuất, bạn có thể sử dụng memory_get_usage. Điều đó sẽ trả về tổng dung lượng bộ nhớ được cấp phát, vì vậy bạn có thể sử dụng một giải pháp thay thế và đo lường mức sử dụng trước và sau để có được việc sử dụng một biến duy nhất. Điều này là một chút hacky, nhưng nó sẽ hoạt động.

$start_memory = memory_get_usage();
$foo = "Some variable";
echo memory_get_usage() - $start_memory;

Lưu ý rằng đây không phải là một phương pháp đáng tin cậy, bạn không thể chắc chắn rằng không có gì khác chạm vào bộ nhớ trong khi gán biến, vì vậy điều này chỉ nên được sử dụng như một giá trị gần đúng.

Bạn thực sự có thể biến điều đó thành một hàm bằng cách tạo một bản sao của biến bên trong hàm và đo bộ nhớ được sử dụng. Chưa thử nghiệm điều này, nhưng về nguyên tắc, tôi không thấy có gì sai với nó:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $tmp = unserialize(serialize($var));
    return memory_get_usage() - $start_memory;
}

14
$tmp = $varsẽ tạo ra một bản sao nông. Điều này sẽ không phân bổ thêm bộ nhớ cho đến khi $ tmp được sửa đổi.
Gordon

@Gordon, bạn nói đúng, tôi đã bỏ qua điểm đó. Vì tôi không thể tìm ra cách thích hợp để sửa đổi biến mà không thay đổi kiểu hoặc kích thước của nó, nên tôi sẽ để nguyên đó. Có lẽ ai đó có thể nghĩ ra một ý tưởng phù hợp :)
Tatu Ulmanen

7
làm thế nào về $tmp = unserialize(serialize($var)); Điều này sẽ kết hợp cách tiếp cận của Aistina ở trên.
Gordon

3
Ngoài ra, vì $varđã là một bản sao cạn hoặc tham chiếu của những gì đã được truyền vào hàm, bạn không cần $tmp, nhưng có thể gán lại cho $var. Điều này lưu tham chiếu nội bộ từ $tmpđến $var.
Gordon

Không có một số cách thanh lịch hơn để bỏ $tmpqua $var?
Tomáš Zato - Phục hồi Monica

24

Không có. Nhưng bạn có thể serialize($var)và kiểm tra strlenkết quả cho một giá trị gần đúng.


Đây là một cách tiếp cận tốt hơn nhiều, vì nó tránh được toàn bộ điều GC.
Gleno

12
Đó là một sự gần đúng khủng khiếp. Mỗi mục trong một mảng trong PHP là ~ 80 byte, nhưng strlen(serialize(array(1,2,3)))là 30.
gsnedders

2
@Aistina, -1. bạn đang đo sai điều. Biến và biến tuần tự là hai thứ hoàn toàn khác nhau và sẽ cho kết quả hoàn toàn khác nhau.
Pacerier

1
Không chỉ vậy, nó sẽ thất bại hoàn toàn trên một số cấu trúc dữ liệu không thể tuần tự hóa, ví dụ: tham chiếu vòng tròn.
duskwuff -inactive- 19/12/15

20

Để trả lời câu trả lời của Tatu Ulmanens:

Cần lưu ý rằng $start_memorychính nó sẽ chiếm bộ nhớ ( PHP_INT_SIZE * 8).

Vì vậy, toàn bộ hàm sẽ trở thành:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $var = unserialize(serialize($var));
    return memory_get_usage() - $start_memory - PHP_INT_SIZE * 8;
}

Xin lỗi để thêm câu trả lời này làm câu trả lời bổ sung, nhưng tôi chưa thể bình luận về câu trả lời.

Cập nhật: * 8 không bị lệch. Nó có thể phụ thuộc vào phiên bản php và có thể là 64/32 bit.


4
Bạn có thể giải thích tại sao * 8? Cảm ơn!
sierrasdetandil

@sierrasdetandil Có vẻ như $ start_memory không chỉ chiếm PHP_INT_SIZEbyte mà là PHP_INT_SIZE*8. Bạn có thể thử điều đó bằng cách gọi hàm này, nó sẽ trả về 0:function sizeofvar() { $start_memory = memory_get_usage(); return memory_get_usage() - $start_memory - PHP_INT_SIZE*8; }
para

8dường như không phải là hằng số. Sau khi hàm nhận xét của bạn trên hệ thống dev của tôi (PHP 5.6.19), nó sẽ trả về -16. Ngoài ra, thú vị là từ php -a, việc gọi hai dòng của hàm cho nhiều giá trị khác nhau.
Paul DelRe

@PaulDelRe vâng, có lẽ nó phụ thuộc vào phiên bản / 64bit kiểu này.
para

bây giờ lỗi nghiêm trọng xảy ra ở cuộc gọi unserialize (). Không giúp được gì đâu! Nếu một biến quá lớn, nó sẽ hết bộ nhớ, việc gọi một hàm trên biến đó sẽ sử dụng nhiều bộ nhớ hơn. :(
john ktejik

4

Xem:

Lưu ý rằng điều này sẽ không cung cấp cho bạn việc sử dụng bộ nhớ của một biến cụ thể. Nhưng bạn có thể đặt các lệnh gọi đến hàm này trước và sau khi gán biến và sau đó so sánh các giá trị. Điều đó sẽ cung cấp cho bạn một ý tưởng về bộ nhớ được sử dụng.

Bạn cũng có thể xem qua Bản ghi nhớ mở rộng PECL , mặc dù tài liệu này hơi thiếu, nếu không muốn nói là hầu như không tồn tại.


Đúng. Nó có thể được sử dụng gián tiếp để trả lời câu hỏi.
Notinlist

3

Bạn có thể chọn tính toán chênh lệch bộ nhớ trên giá trị trả về gọi lại. Đó là một giải pháp thanh lịch hơn có sẵn trong PHP 5.3+.

function calculateFootprint($callback) {
    $startMemory = memory_get_usage();
    $result = call_user_func($callback);
    return memory_get_usage() - $startMemory;
}

$memoryFootprint = calculateFootprint(
    function() {
        return range(1, 1000000);
    }
);

echo ($memoryFootprint / (1024 * 1024)) . ' MB' . PHP_EOL;

3

Bạn không thể tính toán ngược lại dấu chân chính xác của một biến vì hai biến có thể chia sẻ cùng một không gian được cấp phát trong bộ nhớ

Hãy thử chia sẻ bộ nhớ giữa hai mảng, chúng ta thấy rằng việc phân bổ mảng thứ hai tốn một nửa bộ nhớ của mảng đầu tiên. Khi chúng ta bỏ đặt cái đầu tiên, gần như tất cả bộ nhớ vẫn được cái thứ hai sử dụng.

echo memory_get_usage()."\n"; // <-- 433200
$c=range(1,100);
echo memory_get_usage()."\n"; // <-- 444348 (+11148)
$d=array_slice($c, 1);
echo memory_get_usage()."\n"; // <-- 451040 (+6692)
unset($c);
echo memory_get_usage()."\n"; // <-- 444232 (-6808)
unset($d);
echo memory_get_usage()."\n"; // <-- 433200 (-11032)

Vì vậy, chúng ta không thể kết luận rằng mảng thứ hai sử dụng một nửa bộ nhớ, vì nó sẽ trở thành sai khi chúng ta hủy đặt mảng đầu tiên.

Để có cái nhìn đầy đủ về cách bộ nhớ được cấp phát trong PHP và cho mục đích sử dụng, tôi khuyên bạn nên đọc bài viết sau: Các mảng (và giá trị) trong PHP thực sự lớn như thế nào? (Gợi ý: LỚN!)

Kiến thức cơ bản về đếm tham chiếu trong tài liệu PHP cũng có rất nhiều thông tin về việc sử dụng bộ nhớ, và tài liệu tham khảo đếm đến phân đoạn dữ liệu được chia sẻ.

Các giải pháp khác nhau được đưa ra ở đây là tốt cho các phép tính gần đúng nhưng không giải pháp nào có thể xử lý việc quản lý bộ nhớ PHP một cách tinh vi.

  1. tính toán không gian mới được phân bổ

Nếu bạn muốn không gian mới được phân bổ sau một nhiệm vụ, thì bạn phải sử dụng memory_get_usage()trước và sau khi phân bổ, vì việc sử dụng nó với một bản sao sẽ cho bạn một cái nhìn sai lầm về thực tế.

// open output buffer
echo "Result: ";
// call every function once
range(1,1); memory_get_usage();

echo memory_get_usage()."\n";
$c=range(1,100);
echo memory_get_usage()."\n";

Hãy nhớ rằng nếu bạn muốn lưu trữ kết quả của lần đầu tiên memory_get_usage(), thì biến đó phải đã tồn tại trước đó và memory_get_usage()phải được gọi vào một thời điểm khác trước đó và mọi hàm khác cũng vậy.

Nếu bạn muốn lặp lại như trong ví dụ trên, bộ đệm đầu ra của bạn phải được mở sẵn để tránh bộ nhớ kế toán cần thiết để mở bộ đệm đầu ra.

  1. tính toán không gian cần thiết

Nếu bạn muốn dựa vào một hàm để tính toán không gian cần thiết để lưu trữ bản sao của một biến, đoạn mã sau sẽ thực hiện các tối ưu hóa khác nhau:

<?php
function getMemorySize($value) {
    // existing variable with integer value so that the next line
    // does not add memory consumption when initiating $start variable
    $start=1;
    $start=memory_get_usage();
    // json functions return less bytes consumptions than serialize
    $tmp=json_decode(json_encode($value));
    return memory_get_usage() - $start;
}

// open the output buffer, and calls the function one first time
echo ".\n";
getMemorySize(NULL);

// test inside a function in order to not care about memory used
// by the addition of the variable name to the $_GLOBAL array
function test() {
    // call the function name once 
    range(1,1);

    // we will compare the two values (see comment above about initialization of $start)
    $start=1;
    $start=memory_get_usage();
    $c=range(1,100);
    echo memory_get_usage()-$start."\n";
    echo getMemorySize($c)."\n";
}
test();

// same result, this works fine.
// 11044
// 11044

Lưu ý rằng kích thước của tên biến quan trọng trong bộ nhớ được cấp phát.

  1. Kiểm tra mã của bạn !!

Một biến có kích thước cơ bản được xác định bởi cấu trúc C bên trong được sử dụng trong mã nguồn PHP. Kích thước này không dao động trong trường hợp số lượng. Đối với chuỗi, nó sẽ thêm chiều dài của chuỗi.

typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
} zvalue_value;

Nếu chúng ta không tính đến việc khởi tạo tên biến, chúng ta đã biết biến sử dụng bao nhiêu (trong trường hợp là số và chuỗi):

44 byte trong trường hợp số

+ 24 byte trong trường hợp chuỗi

+ độ dài của chuỗi (bao gồm cả ký tự NUL cuối cùng)

(những con số đó có thể thay đổi tùy thuộc vào phiên bản PHP)

Bạn phải làm tròn tối đa 4 byte do căn chỉnh bộ nhớ. Nếu biến nằm trong không gian toàn cục (không nằm trong một hàm), nó cũng sẽ cấp phát thêm 64 byte.

Vì vậy, nếu bạn muốn sử dụng một trong các mã bên trong trang này, bạn phải kiểm tra xem kết quả bằng cách sử dụng một số trường hợp thử nghiệm đơn giản (chuỗi hoặc số) có khớp với những dữ liệu đó có tính đến mọi dấu hiệu trong bài đăng này không (mảng $ _GLOBAL, lệnh gọi hàm đầu tiên, bộ đệm đầu ra, ...)


1
... và đó là ngay cả trước khi chúng ta đi vào nội bộ của zvalue, is_refvà sao chép vào viết. Cảm ơn bạn.
Piskvor rời tòa nhà

1
Cảm ơn bạn, tôi đã bỏ lỡ trang đó trong Hướng dẫn sử dụng PHP. Tôi đã thêm liên kết để hoàn thành câu trả lời của mình (nhưng tôi đoán bạn đã đọc nó).
Adam

2

Tôi gặp sự cố tương tự và giải pháp tôi đã sử dụng là ghi biến vào một tệp rồi chạy filesize () trên đó. Đại khái như thế này (mã chưa được kiểm tra):

function getVariableSize ( $foo ) 
{
    $tmpfile = "temp-" . microtime(true) . ".txt";
    file_put_contents($tmpfile, $foo);
    $size = filesize($tmpfile);
    unlink($tmpfile);
    return $size;
}

Giải pháp này không nhanh khủng khiếp vì nó liên quan đến IO đĩa, nhưng nó sẽ cung cấp cho bạn một cái gì đó chính xác hơn nhiều so với thủ thuật memory_get_usage. Nó chỉ phụ thuộc vào độ chính xác mà bạn yêu cầu.


Cần lưu ý rằng giải pháp này chỉ hoạt động đối với chuỗi và mảng một chiều của chuỗi và việc sử dụng strlensẽ dễ dàng hơn.
Adam


1
function mesure($var){
    $start = memory_get_usage();
    if(is_string($var)){
        $newValue = $var . '';
    }elseif(is_numeric($var)){
        $newValue = $var + 0;
    }elseif(is_object($var)){
        $newValue = clone $var;
    }elseif(is_array($var)){
        $newValue = array_flip($var, []);
    }
    return memory_get_usage() - $start;
}

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.