Rò rỉ bộ nhớ trong PHP là gì?
🥳🥳🥳
// The `count` variable is the number of freed objects during the last gc_collect_cycles() call static void gc_adjust_threshold(int count) { uint32_t new_threshold; /* TODO Very simple heuristic for dynamic GC buffer resizing: * If there are "too few" collections, increase the collection threshold * by a fixed step */ if (count < GC_THRESHOLD_TRIGGER) { /* increase */ if (GC_G(gc_threshold) < GC_THRESHOLD_MAX) { new_threshold = GC_G(gc_threshold) + GC_THRESHOLD_STEP; if (new_threshold > GC_THRESHOLD_MAX) { new_threshold = GC_THRESHOLD_MAX; } if (new_threshold > GC_G(buf_size)) { gc_grow_root_buffer(); } if (new_threshold <= GC_G(buf_size)) { GC_G(gc_threshold) = new_threshold; } } } else if (GC_G(gc_threshold) > GC_THRESHOLD_DEFAULT) { new_threshold = GC_G(gc_threshold) - GC_THRESHOLD_STEP; if (new_threshold < GC_THRESHOLD_DEFAULT) { new_threshold = GC_THRESHOLD_DEFAULT; } GC_G(gc_threshold) = new_threshold; } }1 không còn cần thiết nữa và lập lịch trình GC được cải thiện phần lớn ngay cả trong các trường hợp khác, giúp giảm đáng kể dung lượng bộ nhớ của chúng tôi. 🥳🥳🥳 Nếu lỗi này nghe có vẻ quen thuộc, thì bài đăng này là dành cho bạn. Vấn đề với thông báo này là nó không cho bạn biết nhiều. nó không cho bạn biết tất cả bộ nhớ đã được phân bổ ở đâu. Định vị những nơi tiêu thụ nhiều bộ nhớ trong các hệ thống lớn và phức tạp là điều không dễ dàng. May mắn thay, có một số công cụ có thể giúp tìm mã có vấn đề. Trong bài đăng này, chúng tôi sẽ đề cập đến hai phương pháp để tìm các vị trí trong chương trình của bạn, nơi có nhiều bộ nhớ được phân bổ Chạy ví dụChúng tôi sẽ sử dụng đoạn mã sau làm ví dụ đang chạy. Mục đích của mã là tìm Nemo. Có hai chức năng. một cái đọc tệp có thể định vị Nemo và cái còn lại cố gắng tìm một dòng có nội dung bằng 'nemo'. Vấn đề với đoạn mã này là đôi khi nó tiêu tốn quá nhiều bộ nhớ. Không phải lúc nào cũng vậy, nhưng với một số tệp nhất định, chương trình sẽ gặp sự cố
Tất cả các kỹ thuật và công cụ được mô tả là không cần thiết để giải quyết vấn đề này (bạn đã có thể phát hiện ra vấn đề chưa?), nhưng nó cho phép chúng tôi xem các công cụ và kỹ thuật đó hoạt động như thế nào trong thực tế memory_get_usage()PHP có hai chức năng có thể cho bạn biết điều gì đó về việc sử dụng bộ nhớ của chương trình của bạn. 0, thì memory_get_usage trả về tổng dung lượng bộ nhớ thực sự được phân bổ từ hệ điều hành, nhưng một phần trong số đó có thể chưa (chưa) được chương trình của bạn sử dụng. Nếu nó được đặt thành 2, nó sẽ trả về số byte mà PHP đã yêu cầu (và nhận) từ hệ điều hành và thực tế đang được chương trình sử dụng. Mệnh đề sau luôn đúng. 3. Bộ nhớ được yêu cầu theo khối, không phải lúc nào cũng được sử dụng đầy đủMột lợi thế của việc sử dụng các chức năng này là chúng thực sự dễ sử dụng. Một trong những cách khả thi để tìm ra lỗi rò rỉ bộ nhớ của bạn là phân tán lệnh gọi tới ____26_______ trên toàn bộ mã của bạn và ghi nhật ký đầu ra của nó. Sau đó, bạn có thể thử tìm một mẫu. việc sử dụng bộ nhớ tăng ở đâu? Hãy sử dụng các hàm này để biết được vấn đề hiện tại của chúng ta có thể nằm ở đâu. Trong ví dụ bên dưới, tôi sử dụng các ký tự đánh dấu như 'A', 'B', v.v. để có thể theo dõi một mục nhập nhật ký trở lại một vị trí trong mã. Một tùy chọn khác là đưa 'hằng số kỳ diệu' như 5 và 6 vào đầu ra nhật ký của bạn
Khi chúng tôi chạy ví dụ của mình bây giờ, chúng tôi có đầu ra nhật ký sau 7 8 9 0Nó thật thú vị. cho đến điểm đánh dấu A, không có vấn đề gì. Giữa dòng A và B, nỗi nhớ chợt dâng trào. Các điểm đánh dấu này tương ứng với phần đầu và phần cuối của hàm 1. Hãy thử xác nhận giả thuyết này bằng một kỹ thuật khácTrình hồ sơ XdebugLà một lập trình viên PHP, chắc hẳn bạn đã từng nghe đến (và sử dụng) Xdebug. Nếu bạn chưa có, hãy kiểm tra và đảm bảo cài đặt nó. Những gì bạn có thể không biết là nó cũng đi kèm với một hồ sơ. một công cụ cung cấp cái nhìn sâu sắc về hành vi thời gian chạy của một chương trình. Trình lược tả này phức tạp hơn nhiều so với các hàm PHP đã đề cập trước đó. thay vì chỉ cung cấp cho bạn thông tin về lượng bộ nhớ được sử dụng, nó cũng cung cấp thông tin chi tiết về chức năng nào đang thực sự cấp phát bộ nhớ. Đây là một lợi thế so với kỹ thuật trước đó, bởi vì nếu bạn thực sự không biết tìm kiếm vấn đề về bộ nhớ của mình ở đâu, thì bạn sẽ phải phân tán một lượng lớn lệnh gọi tới _______26_______ trên toàn bộ cơ sở mã của mình. Trước khi có thể sử dụng trình hồ sơ, có một số thứ cần được cấu hình trong 3 của bạn. Lưu ý rằng hầu hết các tùy chọn này không thể được đặt trong thời gian chạy bằng cách sử dụng 4Trước tiên, bạn phải kích hoạt trình hồ sơ. Điều này có thể được thực hiện theo hai cách. bằng cách sử dụng 5 hoặc bằng cách sử dụng 6. Khi sử dụng tùy chọn đầu tiên, một hồ sơ sẽ được tạo cho mỗi lần chạy chương trình của bạn. Tùy chọn thứ hai chỉ tạo hồ sơ cho chương trình đang chạy của bạn nếu có biến _____11_______7/ 8 hoặc ____11_______9 được đặt với tên _____26_______0 Bạn cũng phải cho Xdebug biết nơi lưu trữ các tệp đã tạo, sử dụng ____26_______1Có nhiều thứ hơn để định cấu hình, nhưng với những cài đặt này, bạn đã sẵn sàng Bây giờ, hãy chạy lại tập lệnh với trình cấu hình Xdebug được bật. Nếu chúng tôi xem xét thư mục đầu ra đã định cấu hình của bạn, chúng tôi có thể tìm thấy cấu hình được tạo ở đó. Tuy nhiên trước khi mở được chúng ta cần một công cụ khác. qCachegrind dành cho Windows hoặc kCachegrind dành cho Linux. Tôi sẽ sử dụng qCachegrind ngay bây giờ Khi mở profile bằng qCachegrind bạn sẽ thấy như hình bên dưới Đảm bảo rằng bạn chọn 'Bộ nhớ' trong menu thả xuống ở đầu cửa sổ, thay vì 'Thời gian' (tùy chọn này có thể hữu ích nếu hiệu suất là một vấn đề) Mặc dù cấu hình Xdebug có nhược điểm của nó, nhưng nó cho phép chúng tôi xem vị trí (có khả năng) rất nhiều bộ nhớ được phân bổ. Chúng tôi có thể xác nhận giả thuyết của mình, cụ thể là 1 dường như có vấn đề, vì hàm này gọi hai hàm PHP phân bổ nhiều bộ nhớsửa lỗi kịch bảnLưu ý rằng trình lược tả hoặc ghi nhật ký sử dụng bộ nhớ hầu như sẽ không bao giờ cung cấp cho bạn câu trả lời chính xác về vấn đề bộ nhớ của bạn nằm ở đâu hoặc ở đâu. Phân tích thủ công sẽ luôn là một phần trong quy trình sửa lỗi của bạn. Tuy nhiên, các công cụ này giúp bạn hình thành ý tưởng về vấn đề có thể xảy ra. Tại thời điểm này, chúng tôi biết chức năng nào có thể có vấn đề. Khi phân tích kỹ hơn về hàm 1, chúng ta có thể thấy rằng hàm này sử dụng một mảng để đệm toàn bộ tệp. Nếu tệp lớn, chương trình sẽ hết bộ nhớ. Bây giờ chúng tôi có đủ thông tin để sửa nóHãy làm việc với giả định rằng 1 cũng được sử dụng ở nơi khác và hành vi của nó sẽ không thay đổi. May mắn thay, có một giải pháp cho vấn đề này. chúng tôi không thực sự phải tải toàn bộ tệpMột cách tương đối đơn giản để giải quyết vấn đề này là sử dụng Trình tạo. Bài đăng tuyệt vời này mô tả khái niệm chi tiết hơn Nói tóm lại, Trình tạo cho phép bạn viết một trình vòng lặp cơ bản, nơi bạn có quyền kiểm soát thông tin nào cần thiết trong bộ nhớ. Khi lặp qua một trình vòng lặp, vòng lặp sẽ kiểm soát thời điểm nó tìm nạp mục tiếp theo từ trình vòng lặp. Vì trình vòng lặp biết cách tìm nạp mục tiếp theo, nên nó không nhất thiết phải có tất cả các mục trong bộ nhớ. Trong ví dụ này, điều này có nghĩa là sẽ chỉ có một dòng tệp trong bộ nhớ tại một thời điểm Hãy xem mã ví dụ ở trên, với Trình tạo
Thật thú vị, hàm 1 đã thay đổi. nó hiện chứa câu lệnh memory_get_peak_usage 3Khi ghi nhật ký sử dụng bộ nhớ cho đoạn mã này, chúng tôi có thể thấy rằng mức sử dụng vẫn ở mức thấp. Tuy nhiên, vì Xdebug tạo ra một cấu hình bộ nhớ tích lũy, cấu hình Xdebug sẽ ít nhiều giống nhau. Điều này xảy ra bởi vì tổng cộng, hàm 1 thực sự phân bổ cùng một lượng bộ nhớ. Tuy nhiên, chức năng hiện giải phóng bộ nhớ được phân bổ sớm hơn, dẫn đến việc sử dụng bộ nhớ nói chung thấp hơn nhiều so với phiên bản trước. Đây là một trong những nhược điểm của việc sử dụng Xdebug profiler. Trong bài đăng tiếp theo, tôi sẽ trình bày cách sử dụng php-memory-profiler để tạo một loại cấu hình bộ nhớ khácPhần kết luậnTrong bài đăng này, chúng ta đã thấy hai phương pháp xác định vị trí trong chương trình PHP của bạn, nơi có nhiều bộ nhớ được phân bổ. đầu tiên, bằng cách sử dụng hàm |