HeSanbi
Nam Nhân✨Hiếu Lễ Nghĩa Trí Tín✨
- Tham gia
- 24/2/13
- Bài viết
- 2,901
- Được thích
- 4,718
- Giới tính
- Nam
Bài viết này chia sẻ chuẩn hóa trong quản lý bộ nhớ cho VBA, giúp tối ưu mã để tránh gây quá tải bộ nhớ trong quá trình mã thực thi.
Tại sao cần quản lý bộ nhớ thủ công, mà không để cho VBA tự động dọn dẹp. Thực ra VBA có trình Runtime để dọn dẹp bộ nhớ, tuy nhiên vấn đề nằm ở cách trình dọn dẹp này hoạt động. Nếu như mã của bạn đang thực thi, bạn chủ quan không dọn dẹp, nghĩ rằng VBA tự động, nhưng tùy trường hợp VBA sẽ hẹn thời gian dọn dẹp, không phải dọn dẹp sau khi phương thức hoàn thành.
1. Dùng mảng hoặc chuỗi quá lớn mà không xóa hoặc chia nhỏ.
Tránh dùng
Giải pháp
2. Dùng chuỗi rỗng ("") trong thay thế và nối chuỗi.
Tránh dùng
Giải pháp
Tránh dùng
Giải pháp (Xem phần nâng cao)
Tránh dùng
Giải pháp
Tránh dùng
Giải pháp
6. Xử lý dữ liệu trực tiếp trên worksheet thay vì mảng
Tránh dùng
Giải pháp
Tránh
Giải pháp
1. Nối chuỗi xử lý chuỗi dùng hàm MID
2. Dùng Win32 API thay cho hàm Mid và ASCW để lấy mã Unicode nếu xử lý chuỗi lớn
Hướng dẫn trên đây giúp các bạn phòng tránh rủi ro về bộ nhớ trong lập trình VBA. Điều này rất quan trọng, vì bộ nhớ là có giới hạn, lập trình quản lý bộ nhớ tốt sẽ giúp ứng dụng chạy mượt hơn, an toàn hơn.
Đồng thời tránh rủi ro cho người sử dụng.
Xem thêm bài viết:
VBA Nâng cao: các lỗi viết mã và cách tránh lỗi ứng dụng bị xem là có virus
Tham khảo thêm các bài viết về vba nâng cao tại tag #vba nâng cao
Tại sao cần quản lý bộ nhớ thủ công, mà không để cho VBA tự động dọn dẹp. Thực ra VBA có trình Runtime để dọn dẹp bộ nhớ, tuy nhiên vấn đề nằm ở cách trình dọn dẹp này hoạt động. Nếu như mã của bạn đang thực thi, bạn chủ quan không dọn dẹp, nghĩ rằng VBA tự động, nhưng tùy trường hợp VBA sẽ hẹn thời gian dọn dẹp, không phải dọn dẹp sau khi phương thức hoàn thành.
Các lỗi thường mắc gây tốn kém bộ nhớ khi viết mã VBA
1. Dùng mảng hoặc chuỗi quá lớn mà không xóa hoặc chia nhỏ.
Chuỗi và mảng trong VBA là hai dạng dữ liệu được cấp phát bộ nhớ không cố định như các kiểu số, logic.
Chính vì vậy nếu dùng sai cách sẽ gây tốn kém bộ nhớ.
JavaScript:
Sub test()
Dim Arr
Arr = [A1:Z1000]
...
End Sub
JavaScript:
Sub test()
Dim Arr, s$
s = String(1000000, "a")
Arr = Split(STRConv(s,vbUnicode), vbNullChar)
End Sub
JavaScript:
Sub test()
Dim Arr
Arr = [A1:Z1000]
...
Erase Arr
End Sub
JavaScript:
Sub test()
Dim Arr, s$
s = String(1000000, "a")
Arr = Split(STRConv(s,vbUnicode), vbNullChar)
...
Erase Arr
s = vbNullString
End Sub
2. Dùng chuỗi rỗng ("") trong thay thế và nối chuỗi.
Chuỗi rỗng, cần lưu ý chuỗi "" là chuỗi có tạo ra vùng nhớ nhưng độ dài là 0.
vbNullString là một con trỏ Null (không trỏ đến chuỗi nào).
JavaScript:
For i = 1 To 10000
Arr(i, 2) = Replace(Arr(i, 2), "a" , "")
Next
JavaScript:
s = JOIN(Arr, "")
JavaScript:
For i = 1 To 10000
Arr(i, 2) = Replace(Arr(i, 2), "a" , vbNullString)
Next
Erase Arr
JavaScript:
s = JOIN(Arr, vbNullString)
...
s = vbNullString
3. Nối chuỗi dùng mảng
Nối chuỗi dùng mảng không gây vấn đề lớn nhưng vẫn nên tránh cho một số trường hợp
JavaScript:
Dim Arr, s$
ReDim Arr(1 To 10000)
For i = 1 To 1000
Arr(i) = "a"
Next
s = JOIN(Arr, vbNullString)
4. Dùng vòng lặp số duyệt qua thành phần, thay vì dùng For Each truy cập bộ nhớVòng lặp For each trong VBA có thể khiến cho bộ nhớ của đối tượng không được giải phóng
JavaScript:
Dim ms As Object, m as Object
Set Ms = regex.Execute("...")
For Each m In Ms
Next
Set Ms = Nothing
Set m = Nothing
JavaScript:
Dim ms As Object, m as Object, i&
Set Ms = regex.Execute("...")
For i = 0 To Ms.Count - 1
Set m = Ms(i)
Next
Set Ms = Nothing
Set m = Nothing
5. Dùng With nhưng thoát sớm không đi qua lệnh End WithLuôn luôn cho mã chạy qua End With trước khi rời khỏi
JavaScript:
Dim ms As Object, m as Object
Set Ms = regex.Execute("...")
With Ms
If .Count = 0 Then Exit Sub
...
End With
Set Ms = Nothing
Set m = Nothing
JavaScript:
Dim ms As Object, m as Object, i&
Set Ms = regex.Execute("...")
With Ms
If .Count = 0 Then GoTo nWith
...
nWith:
End With
Set Ms = Nothing
Set m = Nothing
6. Xử lý dữ liệu trực tiếp trên worksheet thay vì mảng
JavaScript:
For i = 1 to 10000
s = Range("A" & i").value
Next
JavaScript:
Dim Arr
Arr = Range("A1").Resize(10000).value
For i = 1 to 10000
s = Arr(i,1)
Next
Erase Arr
7. Gọi lệnh End giải phóng toàn bộ trạng thái về ban đầuGọi lệnh End khi đang thao tác bộ nhớ với các hàm win32 API, hoặc DLL đã được tải mà chưa được giải phóng.
Nếu Callback Function (Gọi lại hàm bộ nhớ) như OnReadyStateChange của lớp MSXML2.XMLHTTP được khởi tạo và chạy nhưng chưa trả về.
Gọi lệnh End khi đã giải phóng các bộ nhớ liên quan do Lớp và Win32 API khởi tạo.
Tóm lại
Luôn giải phóng tài nguyên, chọn kiểu dữ liệu tối ưu, và xử lý dữ liệu theo từng phần.
- Với chuỗi và mảng: dùng Erase, Empty, hoặc "".
- Với đối tượng (Excel/Access): luôn Close và Set ... = Nothing.
- Với vòng lặp lớn: tái sử dụng biến, tránh tạo mới liên tục.
Phương pháp lập trình nâng cao
1. Nối chuỗi xử lý chuỗi dùng hàm MID
Cấp phát bộ nhớ cho một chuỗi với độ dài cần thiết, sau khi gán với hàm MID xong, cần cắt cho bằng với độ dài cần thiết.
Điều này không gây cấp phát bộ nhớ lặp đi lặp lại nếu dùng toán tử nối chuỗi (&).
*Chú ý: Hàm MID vẫn gây ra lỗi bộ nhớ nếu sử dụng quá tải trong một số trường hợp xử lý ký tự Unicode.
JavaScript:
Sub BuildString()
Dim s As String, i As Long, pos As Long
' Giả sử cần tạo chuỗi gồm 10000 lần "abc"
Dim n As Long
n = 10000
' Cấp phát trước bộ nhớ cho chuỗi (3 ký tự * 10000 = 30000)
s = String$(n * 3, " ") ' tạo chuỗi dài toàn khoảng trắng
pos = 1
For i = 1 To n
Mid$(s, pos, 3) = "abc"
pos = pos + 3
Next i
End Sub
2. Dùng Win32 API thay cho hàm Mid và ASCW để lấy mã Unicode nếu xử lý chuỗi lớn
Trong vòng lặp lớn để lấy mã Unicode của ký tự với Mid và ASCW sẽ gây mã chạy chậm.
Hãy dùng Win32 API sau để thực hiện:
JavaScript:
#If VBA7 Then
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal length As LongPtr)
#Else
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
#End If
Private Sub StringToIntegers(ByVal s As String, refArr() As Integer, Optional BaseArray& = 0)
Dim l As Long
l = Len(s): If l = 0 Then Exit Sub
ReDim refArr(BaseArray To l + BaseArray - 1) As Integer
CopyMemory ByVal VarPtr(refArr(BaseArray)), ByVal StrPtr(s), l * 2
End Sub
Hướng dẫn trên đây giúp các bạn phòng tránh rủi ro về bộ nhớ trong lập trình VBA. Điều này rất quan trọng, vì bộ nhớ là có giới hạn, lập trình quản lý bộ nhớ tốt sẽ giúp ứng dụng chạy mượt hơn, an toàn hơn.
Đồng thời tránh rủi ro cho người sử dụng.
Xem thêm bài viết:
VBA Nâng cao: các lỗi viết mã và cách tránh lỗi ứng dụng bị xem là có virus
Tham khảo thêm các bài viết về vba nâng cao tại tag #vba nâng cao
Lần chỉnh sửa cuối:
