Vì tất cả mã code của chúng tôi đều
chạy rất nhanh và tuyệt vời,
hãy nói thêm một chút về bộ nhớ và cách nó
ảnh hưởng đến hiệu suất trong hệ thống.
Nhiều ngôn ngữ lập trình
được biết đến là gần với phần cứng,
hay đúng hơn là hiệu suất cao,
như C, C ++, và Fortran,
thông thường bản thân các
lập trình viên tự quản lý các bộ nhớ.
Thực ra lập trình viên chịu trách nhiệm
phân bổ khối bộ nhớ
và đôi khi giải phóng nó
trong tương lai khi
họ đã thực sự sử dụng xong.
Vì bạn xác định khi nào và bao nhiêu
bộ nhớ được phân bổ trống nên toàn bộ
chất lượng quản lý bộ nhớ phụ thuộc
vào kỹ năng và hiệu quả của bạn.
Đó đúng là một trách nhiệm lớn.
Trên thực tế các lập trình viên
không luôn làm tốt
việc theo dõi
từng mảnhbộ nhớ như vậy.
Tôi muốn nói, hãy nghĩ rằng
phát triển sản phẩm
là quá trình hỗn độn và rối loạn và thường
bộ nhớ không giải phóng đúng cách.
Những khối bộ nhớ chưa được giải phóng
này gọi là các lỗ hổng bộ nhớ,
chúng nằm quanh
các tài nguyên bị tiêu hao
nơi bạn có thể
tận dụng tốt hơn hoặc ở đâu đó.
Để giảm sự hỗn loạn, căng thẳng
và đôi khi là tổn thất lớn về tiền bạc
gây ra bởi lỗ hổng bộ nhớ, các ngôn ngữ
bộ nhớ có quản lý đã được tạo ra.
Những thời gian chạy của các ngôn ngữ này
theo dõi việc phân bổ bộ nhớ
và giải phóng bộ nhớ trở lại hệ thống
khi nó không còn cần thiết
với chính ứng dụng đó, tất cả không cần
bất kỳ can thiệp nào của lập trình viên.
Nghệ thuật, hay đúng hơn là khoa học dành
lại bộ nhớ trong một môi trường bộ nhớ có
quản lý được gọi là garbage collection,
khái niệm được John McCarthy
nghĩ ra vào năm 1959
nhằm giải quyết các vấn đề
trong ngôn ngữ lập trình lisp.
Có các nguyên tắc cơ bản của
garbage collection như sau, đầu tiên là
Tìm đối tượng dữ liệu chương trình
không thể truy cập trong tương lai,
ví dụ bất kỳ bộ nhớ nào
không còn được tham chiếu bởi bản mã.
Và thứ hai là dành lại các tài nguyên
được sử dụng bởi những đối tượng này.
Khái niệm đơn giản về lý thuyết nhưng
sẽ khá phức tạp khi bạn có tới
hai triệu dòng mã code và
bốn gigs worth phải phân bổ.
Giờ hãy nghĩ về nó, garbage collection
có thể rất nổi trội,
ý tôi là nếu bạn đã có 20.000 phân bổ
trong chương trình của mình lúc này.
Cái nào không còn cần thiết nữa?
Hoặc thậm chí,
khi nào bạn cần
tiến hành một lệnh garbage collection
để giải phóng bộ nhớ
không còn được sử dụng?
Đây thực sự là những
câu hỏi rất khó,
và rất may mắn chúng tôi đã có khoảng
50 năm đổi mới để cải thiện chúng,
đó là lý do garbage collector
trong Runtime của Android
thực sự đã phức tạp hơn một chút
so với đề xuất ban đầu của McCarthy.
Nó được xây dựng để nhanh chóng và
không thể xâm nhập hết mức có thể.
Các khối bộ nhớ trong thời gian chạy của
androids được chia khoảng hiệu quả
dựa vào loại phân bổ
và cách tốt nhất hệ thống có thể tổ chức
phân bổ các sự kiện GC trong tương lai.
Là một đối tượng mới được phân bổ,
những đặc điểm đó được đưa vào sao cho
phù hợp nhất với không gian dành cho nó
tùy theo loại phiên bản thời gian chạy
của android mà bạn đang sử dụng.
Và đây là nội dung quan trọng.
Dù mỗi khoảng có một kích thước cài đặt,
khi đối tượng được phân bổ, chúng tôi vẫn
theo dõi các kích thước tổng hợp
và khi một không gian bắt đầu phát triển,
hệ thống phải thi hành garbage collection
nhằm nỗ lực giải phóng
bộ nhớ cho các phân bổ tương lai.
Giờ thì có thể đưa ra rằng các
sự kiện GC sẽ xử lý khác nhau
tùy vào loại thời gian chạy
Android bạn đang sử dụng.
Ví dụ trong Dalvik, nhiều sự kiện GC
sẽ chặn các sự kiện của toàn hệ thống,
nghĩa là bất kỳ mã quản lý nào đang chạy
đều sẽ dừng lại đến khi nó thi hành xong.
Điều này sẽ trở thành vấn đề nếu các GC
này mất nhiều thời gian hơn bình thường
hoặc cả tấn của chúng đang chạy cùng lúc,
vì nó sẽ ăn vào khung thời gian
của bạn một cách đáng kể.
Và cần biết rằng,
các kỹ sư Android đã dành rất nhiều
thời gian đảm bảo các sự kiện này
chạy nhanh hết mức có thể nhằm giảm
thiểu gián đoạn, điều này được cho là
vẫn có thể gây ra một số vấn đề về
hiệu suất ứng dụng trong mã của bạn.
Thứ nhất, phải hiểu rằng ứng dụng của bạn
càng mất nhiều thời gian với các GC
trong một khung nhất định thì nó càng mất ít
thời gian cho phần logic còn lại
cần có để giữ bạn trong
hàng rào 16 mili giây.
Vì vậy, nếu bạn có rất nhiều GC
hoặc một vài cái rất dài diễn ra kế tiếp,
nó có thể khiến thời gian xử lý khung
của bạn vượt qua 16 mili giây.
việc dựng hàng rào sẽ gây ra những
ràng buộc hoặc bất lợi cho người dùng.
Thứ hai, cần hiểu dòng mã của bạn
có thể đang hoạt động khiến các GC
xảy ra thường xuyên hơn, hoặc khiến chúng
kéo dài lâu hơn thời gian thông thường.
Chẳng hạn nếu bạn đang phân bổ một
kho đối tượng tại phần trong cùng của
một vòng mã chạy trong thời gian dài,
sau đó bạn gây ô nhiễm vùng
bộ nhớ với rất nhiều đối tượng
và bạn sẽ kết thúc
bằng việc khởi động rất nhiều GC
một cách nhanh chóng, do áp lực
bộ nhớ bổ sung này.
Và mặc dù chúng ta đang ở trong
một môi trường bộ nhớ có quản lý,
các lỗ hổng bộ nhớ vẫn có thể xảy ra.
Mặc dù chúng không dễ dàng được
tạo như những ngôn ngữ khác.
Những lỗ hổng này làm ô nhiễm trung tâm
của bạn với các đối tượng không được
giải phóng trong một sự kiện, làm giảm
đáng kể không gian mà bạn được sử dụng
khiến nhiều sự kiện GC khác khởi động
theo cách thông thường do kết quả của nó.
Vì vậy, đó là thỏa thuận,
ý tôi là nếu muốn giảm số lượng sự kiện GC
xảy ra trong một khung nhất định,
bạn cần tập trung vào tối ưu hóa việc
sử dụng bộ nhớ cho các ứng dụng của mình.
Từ góc độ bản mã, có thể sẽ rất khó
để theo dõi nơi mà các vấn đề
tương tự phát sinh,
nhưng thật may mắn, Android SDK có
bộ công cụ mạnh mẽ theo ý bạn.
Hãy cùng xem nhé.