Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 48 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
48
Dung lượng
7,27 MB
Nội dung
Cảm ơn bộ nhớ Hiểu cách JVM sử dụng bộ nhớ riêng trên Windows và Linux như thế nào Andrew Hall, Kỹ sư phần mềm, IBM Tóm tắt: Việc sử dụng hết heap (ND: heap là vùng lưu trữ đặc biệt trong bộ nhớ được dùng để lưu giữ các tài liệu quan trọng như tài nguyên hệ thống và các loại đối tượng khác nhau đang được sử dụng. Các heap đều được giới hạn trong phạm vi 64k. Sau đây gọi là vùng heap) của Java™ không phải là nguyên nhân duy nhất tạo ra một lỗi java.lang.OutOfMemoryError. Nếu bộ nhớ riêng (native memory) dùng hết, có thể xảy ra các lỗi OutOfMemoryError (lỗi thiếu bộ nhớ) mà các kỹ thuật gỡ lỗi thông thường của bạn sẽ không thể giải quyết được. Bài viết này giải thích bộ nhớ riêng là gì, thời gian chạy của Java sử dụng nó như thế nào, việc dùng hết nó sẽ như thế nào và làm thế nào để gỡ lỗi cho một lỗi OutOfMemoryError trên Windows® và Linux®. Một bài viết khác của cùng tác giả trình bày về các chủ đề tương tự nhưng dành cho các hệ thống AIX®. Vùng heap của Java, nơi cấp phát không gian nhớ cho mọi đối tượng Java, là vùng bộ nhớ gắn kết mật thiết nhất với bạn khi viết các ứng dụng Java. Máy ảo Java (JVM) được thiết kế để cách ly chúng ta khỏi các điểm đặc thù của máy chủ, vì thế hoàn toàn tự nhiên, có thể coi vùng heap như là một bộ nhớ. Chắc chắn là bạn đã từng gặp phải một lỗi OutOfMemoryError của vùng heap của Java — gây ra bởi một lỗ rò đối tượng hoặc do không tạo ra vùng heap đủ lớn để lưu trữ tất cả các dữ liệu của bạn — và có lẽ bạn đã học được một vài thủ thuật để gỡ lỗi các kịch bản này. Nhưng khi các ứng dụng Java của bạn xử lý nhiều dữ liệu hơn và nạp công việc đồng thời nhiều hơn, bạn có thể bắt đầu nếm trải các lỗi OutOfMemoryError không thể sửa chữa được khi sử dụng cả túi các thủ thuật thông thường của bạn — đó là các kịch bản trong đó các lỗi xuất hiện ngay cả khi vùng heap của Java chưa đầy. Khi điều này xảy ra, bạn cần phải hiểu những gì đang xảy ra bên trong Môi trường thời gian chạy Java (Java Runtime Environment-JRE) của bạn. Các ứng dụng Java chạy trong môi trường ảo hóa của thời gian chạy (runtime) Java, nhưng thời gian chạy bản thân nó là một chương trình riêng được viết bằng một ngôn ngữ (ví dụ như C), có tiêu dùng tài nguyên riêng, bao gồm cả bộ nhớ riêng. Bộ nhớ riêng là bộ nhớ có sẵn dùng cho tiến trình thời gian chạy, để phân biệt với bộ nhớ của vùng heap Java do một ứng dụng Java sử dụng. Mỗi tài nguyên ảo — bao gồm cả vùng heap Java và các luồng (threads) Java — phải được lưu trữ trong bộ nhớ riêng, cùng với các dữ liệu được các máy ảo sử dụng khi nó chạy. Điều này có nghĩa rằng những hạn chế về bộ nhớ riêng, do phần cứng của máy chủ và hệ điều hành (OS) áp đặt sẽ ảnh hưởng đến những gì bạn có thể làm với ứng dụng Java của bạn. Bài viết này là một trong hai bài trình bày cùng một chủ đề trên các nền tảng hệ thống khác nhau. Trong cả hai bài, bạn sẽ tìm hiểu bộ nhớ riêng là gì, thời gian chạy Java dùng nó như thế nào, việc sử dụng hết nó sẽ ra sao và làm thế nào để gỡ lỗi một OutOfMemoryError riêng. Bài viết này trình bày Windows và Linux và không tập trung vào bất kỳ thời gian chạy cụ thể nào. Bài viết của cùng một tác giả trình bày về AIX và tập trung vào IBM® Developer Kit for Java (Bộ dụng cụ của nhà phát triển của IBM cho Java). (Các thông tin trong bài viết đó về việc thực hiện của IBM cũng đúng cho các nền tảng khác, không phải AIX, vì thế nếu bạn sử dụng IBM Developer Kit cho Java trên Linux hay IBM 32-bit Runtime Environment (Môi trường thời gian chạy 32-bit của IBM cho Windows), bạn có thể nhận thấy bài viết đó cũng có ích). Tóm tắt về bộ nhớ riêng Tôi sẽ bắt đầu bằng cách giải thích những hạn chế về bộ nhớ riêng do hệ điều hành và phần cứng nằm bên dưới áp đặt. Nếu bạn quen với việc quản lý bộ nhớ động trong một ngôn ngữ như C, thì bạn có thể chuyển sang phần tiếp theo. Những hạn chế về phần cứng Rất nhiều hạn chế mà một tiến trình riêng phải trải qua là do phần cứng chứ không phải do hệ điều hành áp đặt. Mỗi máy tính đều có một bộ xử lý và một số bộ nhớ truy cập ngẫu nhiên (RAM), cũng được gọi là bộ nhớ vật lý. Một bộ xử lý dịch dòng dữ liệu thành các lệnh để thực hiện; nó có một hoặc nhiều đơn vị xử lý để thực hiện các phép tính số học số nguyên và dấu phẩy động cũng như nhiều phép tính nâng cao hơn. Một bộ xử lý có một số thanh ghi — đó là các phần tử nhớ rất nhanh được sử dụng làm nơi lưu trữ làm việc khi đang thực hiện các phép tính; kích thước thanh ghi xác định số lượng lớn nhất mà một phép tính đơn lẻ có thể sử dụng. Bộ xử lý được kết nối với bộ nhớ vật lý bằng bus bộ nhớ. Độ lớn của địa chỉ vật lý (địa chỉ được bộ xử lý sử dụng để lập chỉ số RAM vật lý) giới hạn dung lượng bộ nhớ có thể được đánh địa chỉ. Ví dụ, một địa chỉ vật lý 16-bit có thể đánh địa chỉ từ 0x0000 đến 0xFFFF, tạo ra 2^16 = 65536 vị trí nhớ duy nhất. Nếu mỗi địa chỉ trỏ đến một byte của thiết bị lưu trữ, thì một địa chỉ vật lý 16-bit cho phép một bộ xử lý đánh địa chỉ 64KB của bộ nhớ. Các bộ xử lý được mô tả như là một số bit nhất định. Số này thường nói đến kích thước của các thanh ghi, mặc dù có các trường hợp ngoại lệ — như 390 31-bit — ở đây nó nói đến kích thước địa chỉ vật lý. Đối với các nền tảng hệ thống máy tính để bàn và máy chủ, số này là 31, 32 hoặc 64; với thiết bị nhúng và các bộ vi xử lý, nó có thể thấp tới mức bằng 4. Kích thước địa chỉ vật lý có thể giống như độ rộng thanh ghi nhưng có thể lớn hơn hoặc nhỏ hơn. Hầu hết các bộ xử lý 64-bit có thể chạy các chương trình 32-bit khi chạy một hệ điều hành phù hợp. Bảng 1 liệt kê một số các kiến trúc Linux và Windows phổ biến với kích thước thanh ghi và kích thước địa chỉ vật lý của chúng: Bảng 1. Kích thước thanh ghi và địa chỉ vật lý của một số kiến trúc bộ xử lý phổ biến Kiến trúc Độ rộng thanh ghi (bits) Kích thước địa chỉ vật lý (bits) (Modern) Intel® x86 32 32 36 nếu có phần mở rộng địa chỉ vật lý (Pentium Pro và cao hơn) x86 64 64 Hiện tại là 48-bit (có cơ hội để tăng lên sau) PPC64 64 50-bit với POWER 5 390 31-bit 32 31 390 64-bit 64 64 Các hệ điều hành và bộ nhớ ảo Nếu bạn đã viết các ứng dụng để chạy trực tiếp trên bộ xử lý mà không có một hệ điều hành, bạn có thể sử dụng tất cả bộ nhớ mà bộ xử lý có thể đánh địa chỉ cho chúng (giả sử có đủ RAM vật lý được đấu nối). Tuy nhiên, để tận hưởng các tính năng như đa nhiệm và sự trừu tượng của phần cứng, gần như tất cả mọi người đều sử dụng một hệ điều hành nào đó để chạy các chương trình của họ. Trong các hệ điều hành (OS) đa nhiệm như Windows và Linux, có nhiều hơn một chương trình sử dụng tài nguyên hệ thống, bao gồm cả bộ nhớ. Mỗi chương trình cần phải được cấp phát các vùng nhớ vật lý để làm việc trong đó. Có thể thiết kế một hệ điều hành sao cho mọi chương trình làm việc trực tiếp với bộ nhớ vật lý và được tin tưởng sẽ chỉ sử dụng bộ nhớ mà nó đã được cấp. Một số hệ điều hành nhúng làm việc giống như thế, nhưng sẽ là không thích hợp trong một môi trường có nhiều chương trình không được thử nghiệm cùng với nhau vì bất kỳ chương trình nào có thể làm hỏng bộ nhớ của các chương trình khác hoặc của chính hệ điều hành. Bộ nhớ ảo cho phép nhiều tiến trình xử lý dùng chung bộ nhớ vật lý mà không thể làm hỏng dữ liệu của nhau. Trong một hệ điều hành có bộ nhớ ảo (như Windows, Linux và nhiều hệ khác), mỗi chương trình có vùng địa chỉ ảo riêng của nó — một vùng logic của các địa chỉ mà kích thước của nó do kích thước địa chỉ trên hệ thống đó quyết định (như vậy là 31, 32 hoặc 64 bit cho các nền tảng máy tính để bàn và máy chủ). Các vùng trong vùng địa chỉ ảo của một tiến trình có thể được ánh xạ tới bộ nhớ vật lý, đến một tệp hoặc tới bất kỳ thiết bị lưu trữ có đánh địa chỉ khác. Hệ điều hành có thể di chuyển dữ liệu được giữ trong bộ nhớ vật lý đến và ra khỏi một vùng trao đổi (tệp trang (page file) trên Windows hay phân vùng trao đổi (swap partition) trên Linux) khi nó không được sử dụng, để sử dụng bộ nhớ vật lý một cách tốt nhất. Khi một chương trình cố gắng truy cập vào bộ nhớ bằng cách sử dụng một địa chỉ ảo, hệ điều hành kết hợp với phần cứng trên chip ánh xạ địa chỉ ảo đến vị trí vật lý. Vị trí đó có thể là RAM vật lý, một tệp hoặc tệp trang/phân vùng trao đổi. Nếu một vùng bộ nhớ đã được di chuyển tới vùng trao đổi, thì sau đó nó được nạp lại vào bộ nhớ vật lý trước khi được sử dụng. Hình 1 cho thấy bộ nhớ ảo làm việc như thế nào bằng cách ánh xạ các vùng của vùng địa chỉ tiến trình xử lý đến các tài nguyên dùng chung: Hình 1. Bộ nhớ ảo ánh xạ vùng địa chỉ tiến trình tới các tài nguyên vật lý Mỗi cá thể của một chương trình chạy như một tiến trình. Một tiến trình trên Linux và Windows là một tập hợp thông tin về tài nguyên do hệ điều hành kiểm soát (như tệp và thông tin về trình cắm thêm), thường là một vùng địa chỉ ảo (nhiều hơn một vùng trên một số kiến trúc) và ít nhất một luồng thi hành. Kích thước của vùng địa chỉ ảo có thể nhỏ hơn kích thước địa chỉ vật lý của bộ xử lý. Intel x86 32-bit ban đầu có một địa chỉ vật lý 32-bit, cho phép bộ xử lý đánh địa chỉ 4GB của thiết bị lưu trữ. Sau đó, một đặc tính gọi là Physical Address Extension (PAE-Phần mở rộng địa chỉ vật lý) đã được thêm vào để mở rộng kích thước địa chỉ vật lý lên 36-bit — cho phép cài đặt và đánh địa chỉ RAM lên đến 64GB. PAE đã cho phép các hệ điều hành ánh xạ các vùng địa chỉ ảo 4GB 32-bit lên trên một dải địa chỉ vật lý lớn, nhưng nó không cho phép mỗi tiến trình có một vùng địa chỉ ảo 64GB. Điều này có nghĩa là nếu bạn đặt nhiều hơn 4GB bộ nhớ trong một máy chủ Intel 32-bit, bạn không thể ánh xạ tất cả nó trực tiếp vào trong một tiến trình đơn. Tính năng Các phần mở rộng cửa sổ địa chỉ (Address Windowing Extensions) cho phép một tiến trình Windows ánh xạ một phần vùng địa chỉ 32-bit của nó như là một cửa sổ trượt vào trong một vùng bộ nhớ lớn hơn. Linux sử dụng các công nghệ tương tự dựa vào việc ánh xạ các vùng vào trong vùng địa chỉ ảo. Điều này có nghĩa rằng mặc dù bạn không thể trực tiếp tham chiếu nhiều hơn 4GB bộ nhớ, bạn có thể làm việc với các vùng bộ nhớ lớn hơn. Vùng nhân (kernel) và vùng người sử dụng Mặc dù mỗi tiến trình có vùng địa chỉ riêng của mình, một chương trình thường không thể sử dụng tất cả vùng ấy. Các vùng địa chỉ được chia thành vùng người dùng (user space) và vùng nhân (kernel space). Nhân (kernel) là chương trình hệ điều hành chính và chứa đựng logic để giao diện đến phần cứng máy tính, lập lịch trình các chương trình và cung cấp các dịch vụ như làm việc trên mạng và bộ nhớ ảo. Là một phần của quá trình khởi động máy tính, nhân của hệ điều hành chạy và khởi động phần cứng. Một khi nhân đã cấu hình phần cứng và trạng thái bên trong riêng của mình, tiến trình đầu tiên của vùng người dùng mới khởi động. Nếu một chương trình của người dùng cần một dịch vụ từ hệ điều hành, nó có thể thực hiện một hoạt động — có tên là cuộc gọi hệ thống — để nhảy vào trong chương trình nhân (kernel), sau đó chương trình nhân thực hiện yêu cầu. Các cuộc gọi hệ thống thường cần thiết cho các hoạt động như đọc và viết các tệp, làm việc trên mạng và bắt đầu các tiến trình mới. Nhân yêu cầu truy cập vào bộ nhớ riêng của nó và bộ nhớ của tiến trình gọi khi thi hành một cuộc gọi hệ thống. Vì bộ xử lý, đang thi hành luồng hiện tại, được cấu hình để ánh xạ các địa chỉ ảo bằng cách sử dụng ánh xạ vùng địa chỉ cho tiến trình hiện tại, hầu hết các hệ điều hành ánh xạ một phần của mỗi vùng địa chỉ tiến trình tới một vùng bộ nhớ nhân chung. Phần của vùng địa chỉ được ánh xạ để sử dụng bởi nhân được gọi là vùng nhân; phần còn lại, có thể được ứng dụng của người dùng sử dụng, được gọi là vùng người dùng. Sự cân bằng giữa vùng nhân và vùng người dùng khác nhau theo hệ điều hành và thậm chí khác nhau cả trong các cá thể của cùng một hệ điều hành chạy trên kiến trúc phần cứng khác nhau. Sự cân bằng thường cấu hình được và có thể được điều chỉnh để cung cấp thêm vùng cho các ứng dụng của người dùng hay cho nhân. Việc thu nhỏ vùng nhân có thể gây ra các vấn đề như hạn chế số lượng người sử dụng có thể đăng nhập cùng lúc hoặc số các tiến trình có thể chạy; vùng người sử dụng nhỏ hơn có nghĩa là lập trình viên ứng dụng có phạm vi làm việc nhỏ hơn Theo mặc định, Windows 32-bit có một vùng người dùng 2GB và một vùng nhân 2GB. Sự cân bằng này có thể được thay đổi thành một vùng người sử dụng 3GB và một vùng nhân 1GB trên một số phiên bản của Windows bằng cách thêm khóa chuyển đổi /3GB vào cấu hình khởi động và liên kết lại các ứng dụng bằng khóa chuyển đổi /LARGEADDRESSAWARE. Trong Linux 32-bit, giá trị mặc định là vùng người sử dụng 3GB và vùng nhân 1GB. Một số bản phân phối Linux cung cấp một nhân hugemem hỗ trợ một vùng người sử dụng 4GB. Để đạt được điều này, nhân được cung cấp một vùng địa chỉ của riêng nó, được sử dụng khi một cuộc gọi hệ thống được bắt đầu. Cái lợi về vùng người dùng phải trả giá bằng các cuộc gọi hệ thống chậm hơn vì hệ điều hành phải sao chép dữ liệu giữa các vùng địa chỉ và thiết lập lại các ánh xạ vùng-địa chỉ tiến trình mỗi khi một cuộc gọi hệ thống bắt đầu. Hình 2 cho thấy bố trí vùng địa chỉ cho Windows 32-bit: Hình 2. Bố trí vùng địa chỉ cho Windows 32-bit Hình 3 cho thấy các bố trí vùng-địa chỉ cho Linux 32-bit: Hình 3. Bố trí vùng-địa chỉ cho Linux 32-bit Một vùng địa chỉ nhân riêng biệt cũng được sử dụng trên Linux 390 31-bit, trong đó vùng địa chỉ nhỏ hơn 2GB làm cho việc phân chia một vùng địa chỉ duy nhất là không nên, tuy nhiên, kiến trúc 390 có thể làm việc với nhiều vùng địa chỉ đồng thời mà không làm hiệu năng giảm sút. Vùng địa chỉ tiến trình phải có mọi thứ mà một chương trình đòi hỏi — bao gồm chính chương trình đó và các thư viện dùng chung (các DLL trên Windows, các tệp .so trên Linux) mà nó sử dụng. Các thư viện dùng chung không chỉ có thể chiếm vùng mà một chương trình không thể sử dụng để lưu trữ dữ liệu vào nữa, chúng cũng còn có thể phân mảnh vùng địa chỉ và giảm số lượng bộ nhớ có thể được cấp phát như là một đoạn liên tục. Điều này là dễ nhận thấy trong các chương trình chạy trên Windows x86 với một vùng người sử dụng 3GB. Các DLL được xây dựng với một địa chỉ nạp vào ưa thích: khi một DLL được nạp, nó được ánh xạ vào vùng địa chỉ tại một vị trí cụ thể trừ khi vị trí đó đã bị chiếm, trong trường hợp này nó được bố trí lại và được nạp vào nơi khác. Với vùng người sử dụng 2GB có sẵn khi Windows NT được thiết kế ban đầu, việc xây dựng các thư viện hệ thống để nạp vào gần ranh giới 2GB là có ý nghĩa — làm như thế sẽ để lại hầu hết vùng người sử dụng tự do cho ứng dụng sử dụng. Khi vùng người dùng được mở rộng đến 3GB, các thư viện dùng chung hệ thống vẫn nạp ở gần 2GB — bây giờ nằm ở giữa vùng người dùng. Mặc dù có một vùng người dùng tổng cộng 3GB, không thể cấp phát một khối 3GB của bộ nhớ vì các thư viện dùng chung đã ở trong đó rồi. Sử dụng khóa chuyển đổi /3GB trên Windows làm giảm vùng nhân tới một nửa so với những gì nó đã được thiết kế ban đầu. Trong một số kịch bản có thể dùng hết vùng nhân 1GB và nếm trải vào/ra (I/O) chậm hoặc các vấn đề khi tạo phiên người dùng mới. Mặc dù khóa chuyển đổi /3GB có thể rất có giá trị cho một số ứng dụng, bất cứ môi trường nào khi sử dụng nó cần được kiểm tra tải kỹ lưỡng trước khi được triển khai. Xem Tài nguyên với các đường liên kết đến nhiều thông tin hơn về khóa chuyển đổi /3GB và các lợi thế và các bất lợi của nó. Lỗi lỗ rò bộ nhớ riêng hoặc sử dụng bộ nhớ riêng quá mức sẽ gây ra những vấn đề khác nhau tùy thuộc vào việc bạn tận dụng hết vùng địa chỉ hay là chạy hết bộ nhớ vật lý. Việc cạn kiệt vùng địa chỉ thường xảy ra chỉ với các tiến trình 32-bit — vì tối đa 4GB dễ dàng cấp phát. Một tiến trình 64-bit có một vùng người sử dụng bằng hàng trăm hoặc hàng ngàn gigabyte, rất khó để lấp đầy ngay cả khi bạn cố gắng. Nếu bạn dùng hết vùng địa chỉ của một tiến trình Java, thì sau đó thời gian chạy Java có thể bắt đầu hiển thị các triệu chứng kỳ lạ mà tôi sẽ mô tả sau trong bài viết này. Khi chạy trên một hệ thống có nhiều vùng địa chỉ tiến trình hơn bộ nhớ vật lý, một lỗ rò bộ nhớ hoặc việc sử dụng quá mức bộ nhớ riêng buộc hệ điều hành trao đổi, đưa ra thiết bị lưu trữ hậu thuẫn một số vùng địa chỉ ảo của tiến trình riêng. Truy cập vào một địa chỉ bộ nhớ đã được trao đổi đưa ra ngoài chậm hơn nhiều so với đọc địa chỉ đang thường trú (trong bộ nhớ vật lý) vì hệ điều hành phải lấy dữ liệu ra từ đĩa cứng. Có thể cấp phát đủ bộ nhớ để tận dụng hết tất cả bộ nhớ vật lý và tất cả bộ nhớ trao đổi (vùng phân trang); trên Linux, điều này sẽ kích hoạt trình sát thủ hết bộ nhớ (OOM) nhân, trình sát thủ này buộc phải giết tiến trình thiếu bộ nhớ nhất. Trên Windows, việc cấp phát bắt đầu thất bại theo cùng một cách như chúng đã xảy ra nếu vùng địa chỉ đã đầy. Nếu bạn đồng thời cố gắng sử dụng nhiều bộ nhớ ảo hơn bộ nhớ vật lý hiện có, dĩ nhiên vấn đề xuất hiện sớm hơn nhiều trước khi tiến trình này bị giết vì dùng quá bộ nhớ. Hệ thống sẽ luẩn quẩn — dành phần lớn thời gian của nó sao chép bộ nhớ quay đi quay lại từ vùng trao đổi. Khi điều này xảy ra, hiệu năng của máy tính và các ứng dụng riêng lẻ sẽ trở nên tồi tệ đến mức người dùng không thể không nhận thấy đã có vấn đề. Khi một vùng heap Java của JVM bị trao đổi ra, hiệu năng của các bộ thu gom dữ liệu rác trở nên cực kỳ kém, đến mức mà ứng dụng có thể bị treo. Nếu nhiều thời gian chạy Java đồng thời đang chạy trên một máy tính tại cùng một thời điểm, thì bộ nhớ vật lý phải đủ để chứa hết tất cả các vùng heap của Java. Thời gian chạy của Java sử dụng bộ nhớ riêng như thế nào Thời gian chạy của Java là một tiến trình hệ điều hành chịu các ràng buộc của phần cứng và hệ điều hành mà tôi nêu trong phần trước. Các môi trường thời gian chạy cung cấp các khả năng theo đòi hỏi của mã của người sử dụng còn chưa biết; điều này làm cho không thể dự đoán môi trường thời gian chạy sẽ đòi hỏi tài nguyên nào trong mỗi tình huống. Mỗi hành động mà một ứng dụng Java thực hiện bên trong môi trường Java được quản lý đều có khả năng có thể ảnh hưởng đến các yêu cầu tài nguyên của thời gian chạy cung cấp môi trường đó. Phần này mô tả các ứng dụng Java tiêu dùng bộ nhớ riêng như thế nào và tại sao. Vùng heap của Java và việc thu dọn dữ liệu rác Vùng heap của Java là vùng bộ nhớ mà các đối tượng được cấp phát ở đó. Hầu hết các triển khai thực hiện Java SE có một vùng heap lôgic, mặc dù một số thời gian chạy Java chuyên biệt, ví dụ như là triển khai thực hiện Đặc tả thời gian thực cho Java (Real Time Specification for Java -RTSJ) có nhiều vùng heap. Một vùng heap vật lý có thể được chia một cách lô gic thành các phần tùy thuộc vào thuật toán thu dọn dữ liệu rác (GC) được sử dụng để quản lý bộ nhớ của vùng heap. Những phần này thường được triển khai thực hiện như các ô nhớ liền khối của bộ nhớ riêng dưới sự kiểm soát của trình quản lý bộ nhớ Java (bao gồm các bộ thu dọn dữ liệu rác). Kích thước của vùng heap được điều khiển từ dòng lệnh Java bằng cách sử dụng các tuỳ chọn -Xmx và -Xms (mx là kích thước tối đa của vùng heap, ms là kích thước ban đầu). Mặc dù vùng heap lô-gic (vùng bộ nhớ được sử dụng thực sự) có thể tăng lên và thu nhỏ theo số lượng các đối tượng trên vùng heap và thời gian dành cho GC, dung lượng bộ nhớ riêng được sử dụng vẫn không đổi và được quyết định bởi giá trị -Xmx: kích thước vùng heap tối đa. Hầu hết các thuật toán GC dựa trên vùng heap đang được cấp phát như một dãy ô nhớ liền khối của bộ nhớ, do đó không thể cấp phát thêm nhiều bộ nhớ riêng khi vùng heap cần mở rộng. Tất cả bộ nhớ của vùng heap phải được dự trữ trước. Việc dự trữ bộ nhớ riêng không giống như việc cấp phát nó. Khi bộ nhớ riêng được dự trữ, nó không được hậu thuẫn bởi bộ nhớ vật lý hoặc thiết bị lưu trữ khác. Mặc dù việc dự trữ các đoạn của vùng địa chỉ sẽ không làm cạn kiệt tài nguyên vật lý, nhưng nó ngăn cản không cho bộ nhớ đó được sử dụng cho các mục đích khác. Lỗ rò do việc dự trữ bộ nhớ gây ra vì không bao giờ được sử dụng cũng nghiêm trọng không kém lỗ rò bộ nhớ được cấp phát. [...]... cách sử dụng bộ nhớ của một tiến trình rất có ích, bạn sẽ hiểu biết nhiều hơn về bộ nhớ riêng đang được sử dụng như thế nào bằng cách vẽ biểu đồ việc sử dụng bộ nhớ theo thời gian Một cách để làm điều này là sử dụng GCMV Trình giám sát bộ nhớ và thu dọn dữ liệu rác (GCMV) ban đầu được viết để vẽ biểu đồ dựa trên các bản ghi nhật ký GC đầy đủ thông báo, cho phép người dùng xem các thay đổi trong cách sử. .. sánh được với mã riêng Việc biên dịch Bytecode sử dụng bộ nhớ riêng (giống như cách mà một trình biên dịch tĩnh như là gcc đòi hỏi bộ nhớ để chạy), nhưng cả đầu vào (bytecode) lẫn đầu ra (mã thực thi) từ JIT cũng phải được lưu trữ trong bộ nhớ riêng Các ứng dụng Java có chứa nhiều phương thức được biên dịch tức thời (JIT) sử dụng nhiều bộ nhớ riêng hơn các ứng dụng nhỏ hơn Các lớp và các trình nạp... thấy cách sử dụng toàn bộ vùng địa chỉ Nếu lượng sử dụng đạt đến gần giới hạn của vùng người dùng (từ 2 đến 3GB như thảo luận trước đây), bạn sẽ có nhiều khả năng gặp tình trạng thiếu bộ nhớ riêng Linux không có công cụ PerfMon tương đương, nhưng bạn có một vài lựa chọn Các công cụ dòng lệnh như ps, top và pmap có thể hiển thị dấu vết bộ nhớ riêng của ứng dụng Mặc dù nhận được một ảnh chụp nhanh về cách. .. dù số lượng bộ nhớ được sử dụng cho mỗi luồng là khá nhỏ, đối với một ứng dụng có hàng trăm luồng, tổng bộ nhớ sử dụng cho các ngăn xếp luồng có thể lớn Việc chạy một ứng dụng với nhiều luồng hơn số các bộ xử lý có sẵn để chạy chúng thường không hiệu quả và có thể dẫn đến hiệu năng kém cũng như việc sử dụng bộ nhớ tăng lên Tôi có thể nói như thế nào nếu tôi đang dùng hết bộ nhớ riêng? Một thời gian chạy... cho một vùng bộ nhớ cụ thể và cung cấp một cách để xác định vị trí đoạn mã cấp phát bộ nhớ mà sau này không được giải phóng Tôi giới thiệu cho bạn bài viết "Umdhtools.exe: Sử dụng Umdh.exe như thế nào để tìm các lỗ rò bộ nhớ trên Windows" để tìm chỉ dẫn về cách sử dụng UMDH (xem Tài nguyên) Trong bài này, tôi sẽ tập trung vào kết quả đầu ra của UMDH trông thế nào khi chạy nó với một ứng dụng JNI có... về bộ nhớ riêng Nếu bạn nghi ngờ bạn có một lỗ rò bộ nhớ riêng hoặc bạn muốn hiểu chính xác bộ nhớ của bạn chạy đi đâu, có một số công cụ có thể giúp bạn Microsoft cung cấp công cụ UMDH (user-mode dump heap – heap kết xuất của chế độ người sử dụng) và công cụ LeakDiag để gỡ lỗi tăng bộ nhớ riêng trên Windows (xem Tài nguyên) Cả hai đều làm việc theo cùng cách tương tự như nhau: ghi lại tuyến mã nào. .. nào để nhận biết sự cạn kiệt bộ nhớ riêng Thay vào đó, bạn cần phải sử dụng dữ liệu từ hệ điều hành và từ thời gian chạy Java để xác định chẩn đoán đó Ví dụ về thiếu bộ nhớ riêng Để giúp bạn thấy việc cạn kiệt bộ nhớ riêng ảnh hưởng đến việc triển khai thực hiện Java mà bạn đang sử dụng như thế nào, mã mẫu của bài viết này (xem Tải về) có chứa một số chương trình Java gây ra sự cạn kiệt vùng heap riêng. .. còn trên cơ sở là bạn sẽ khởi động lại ứng dụng của mình trước khi vùng địa chỉ tiến trình bị cạn kiệt Cái gì đang sử dụng bộ nhớ riêng của tôi? Một khi bạn đã xác định bạn đang cạn kiệt bộ nhớ riêng, câu hỏi hợp lý tiếp theo là: cái gì đang sử dụng bộ nhớ đó? Việc trả lời câu hỏi này rất khó bởi vì, theo mặc định, Windows và Linux không lưu trữ thông tin về tuyến mã nào được cấp phát một đoạn bộ nhớ. .. việc sử dụng càng nhiều lớp hơn thì càng sử dụng nhiều bộ nhớ hơn (Điều này có thể có nghĩa là việc sử dụng bộ nhớ riêng của bạn tăng lên hoặc bạn phải thay đổi kích thước một vùng một cách rõ ràng — chẳng hạn như PermGen hoặc bộ nhớ sẵn (cache) của lớp-dùng chung — để cho phép chứa hết tất cả các lớp) Hãy nhớ rằng không chỉ cần chứa hết ứng dụng của bạn; mà các khung công tác, các máy chủ ứng dụng, ... xạ bộ nhớ nào được thực hiện bởi mã riêng đều lấy bộ nhớ từ thời gian chạy Java Một số hàm JNI nhất định có thể sử dụng bộ nhớ riêng như là một phần hoạt động bình thường của chúng Các hàm GetTypeArrayElements và GetTypeArrayRegion có thể sao chép dữ liệu của vùng heap của Java vào các bộ đệm của bộ nhớ riêng để cho mã riêng làm việc với chúng Việc có tạo ra một bản sao chép hay không phụ thuộc vào . Cảm ơn bộ nhớ Hiểu cách JVM sử dụng bộ nhớ riêng trên Windows và Linux như thế nào Andrew Hall, Kỹ sư phần mềm, IBM Tóm tắt: Việc sử dụng hết heap (ND: heap là vùng. giải thích bộ nhớ riêng là gì, thời gian chạy của Java sử dụng nó như thế nào, việc dùng hết nó sẽ như thế nào và làm thế nào để gỡ lỗi cho một lỗi OutOfMemoryError trên Windows và Linux . Một. việc cấp phát bộ nhớ riêng hay các việc ánh xạ bộ nhớ nào được thực hiện bởi mã riêng đều lấy bộ nhớ từ thời gian chạy Java. Một số hàm JNI nhất định có thể sử dụng bộ nhớ riêng như là một phần