Lỗi "Out of memory" khi khai báo kích thước mảng

Liên hệ QC
Tôi tuân thủ nội quy khi đăng bài

Thái Phúc

Thành viên mới
Tham gia
1/12/18
Bài viết
29
Được thích
2
Giới tính
Nam
Xin chào các Anh Chị GPE!
Em có thói quen khi khai báo kích thước mảng động 2 chiều là "ReDim Arr(1 To Rows.Count, 1 To 5)". Lâu nay vẫn chạy bình thường trên máy em, nhưng khi chuyển sang máy khác thì bị lỗi "Out of memory" mặc dù các máy dùng cùng đời office. Vậy em muốn hỏi các Anh Chị lý do xảy ra lỗi và việc em khai báo đã đúng chưa. Các anh chị chia sẻ cách các anh chị khai báo mảng khi chưa biết số dòng (Có thể hàng trăm ngàn dòng) với ạ. Em xin cảm ơn
 
"ReDim Arr(1 To Rows.Count, 1 To 5)"
Bạn đang hiểu dòng này như nào vậy? Bạn khai báo vậy mình cũng không có dám xài á.
Rows.Count sẽ trả về tổng số dòng khả dĩ trong sheet tính, với excel 2007 trở về trước vào tầm 65 ngàn dòng, còn sau 2007 định dạng ".xlsm" thì 1048576; --> Bạn đang khai báo 1 con số quá khủng khiếp.
Cách làm là tìm ra dòng cuối của khối dữ liệu và sau đó khai báo bằng với kích thước đó thôi;
JavaScript:
lastRow = Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
ReDim Arr(1 To lastRow, 1 To 5)
 
Upvote 0
Giả sử lastRow = 10000 nhưng dòng cuối của mảng có thể lớn gấp nhiều lần anh ạ
Thì đã cất công đi tìm dòng cuối của khối dữ liệu, còn cái nào cuối hơn của cuối nữa ?
Bạn tìm hiểu dòng
JavaScript:
lastRow = Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
hoặc gửi đính kèm file lên trên này, để mọi người xem và góp ý.
1719996140939.jpeg
 
Upvote 0
Giả sử lastRow = 10000 nhưng dòng cuối của mảng có thể lớn gấp nhiều lần anh ạ
Gấp nhiều lần là gấp mấy lần? Tìm cách xác định số lượng hàng nhỏ nhất nhưng đủ để chứa tất cả dữ liệu của mảng trong mọi trường hợp, đó là số lượng hàng tối ưu để khai báo cho mảng.
 
Upvote 0
@Chủ bài đăng: Bạn chịu khó hỏi nó xem dòng cuối nó tìm ra là bao nhiêu cái đã, trước khi khai báo mảng!
 
Upvote 0
@Chủ bài đăng: Bạn chịu khó hỏi nó xem dòng cuối nó tìm ra là bao nhiêu cái đã, trước khi khai báo mảng!
Mã:
Sub TestKhaiBaoKichThuoc()
Dim i As Long, j As Long, K As Long
Dim sArr, dArr, Arr
Dim lastRow As Long
    ReDim dArr(1 To Rows.Count, 1 To 2)
    lastRow = Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
    sArr = Sheets("Sheet1").Range("A2:C" & lastRow).Value
    
    For i = 1 To UBound(sArr)
        If sArr(i, 3) >= sArr(i, 2) Then
            For j = sArr(i, 2) To sArr(i, 3)
                K = K + 1
                dArr(K, 1) = j
                dArr(K, 2) = sArr(i, 1)
            Next j
        
        End If
    Next i
    
    If K Then
        ReDim Arr(1 To K, 1 To 2)
        For i = 1 To K
            Arr(i, 1) = dArr(i, 1)
            Arr(i, 2) = dArr(i, 2)
        Next i
        
        lastRow = Sheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Row
        Sheets("Sheet2").Range("A2:B" & lastRow).ClearContents
        Sheets("Sheet2").Range("A2").Resize(K, 2).Value = Arr
    End If
    
End Sub
Đây là cách em làm lâu nay khi viết code kiểu này. Em không biết trước số dòng tối đa của mảng nên cứ khai báo ReDim dArr(1 To Rows.Count, 1 To 2), và trên máy em chạy không việc gì. Hôm nay chuyển file sang máy khác (Cùng đời office) thì bị lỗi. Mong các anh chị giúp đơ ạ!
 

File đính kèm

  • Test khai bao kich thuoc mang.xlsm
    162.3 KB · Đọc: 7
Upvote 0
một sheet là 1 triệu dòng (gọi vậy cho chẵn)
(1000000, 5) x 16 là 80000000, chả là gì với memory hiện nay.
Có cái gì đó trong "máy khác" của thớt.

Mảng cần một khoảng memory liên tục.
Cái "máy khác" của thớt có thể bị page size thì nhỏ mà "disk fragmentation" nhiều quá.
Đem ra defragment, và chỉnh lại lượng pages thử, may ra còn xài được.
 
Upvote 0
Dung lượng RAM khả dụng trên máy tính đó mà không đủ cho mảng 5 cột thì chỉ có cách tăng dung lượng RAM khả dụng lên thôi.

Bởi số lượng dòng tối ưu cũng sẽ có lúc rơi vào trường hợp dùng tới mức lớn đó.
 
Upvote 0
Dung lượng RAM khả dụng trên máy tính đó mà không đủ cho mảng 5 cột thì chỉ có cách tăng dung lượng RAM khả dụng lên thôi.

Bởi số lượng dòng tối ưu cũng sẽ có lúc rơi vào trường hợp dùng tới mức lớn đó.
Vậy có cách gì để "bẫy lỗi" việc này không các Anh Chị. Ví dụ ban đầu vẫn khai báo là "ReDim dArr(1 To Rows.Count, 1 To 2)". Nếu nó xảy ra lỗi thì sẽ khai báo thành " ReDim dArr(1 To 65536, 1 To 2)" chẳng hạn?
 
Upvote 0
Mã:
Sub TestKhaiBaoKichThuoc()
Dim i As Long, j As Long, K As Long
Dim sArr, dArr, Arr
Dim lastRow As Long
    ReDim dArr(1 To Rows.Count, 1 To 2)
    lastRow = Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
    sArr = Sheets("Sheet1").Range("A2:C" & lastRow).Value
   
    For i = 1 To UBound(sArr)
        If sArr(i, 3) >= sArr(i, 2) Then
            For j = sArr(i, 2) To sArr(i, 3)
                K = K + 1
                dArr(K, 1) = j
                dArr(K, 2) = sArr(i, 1)
            Next j
       
        End If
    Next i
   
    If K Then
        ReDim Arr(1 To K, 1 To 2)
        For i = 1 To K
            Arr(i, 1) = dArr(i, 1)
            Arr(i, 2) = dArr(i, 2)
        Next i
       
        lastRow = Sheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Row
        Sheets("Sheet2").Range("A2:B" & lastRow).ClearContents
        Sheets("Sheet2").Range("A2").Resize(K, 2).Value = Arr
    End If
   
End Sub
Đây là cách em làm lâu nay khi viết code kiểu này. Em không biết trước số dòng tối đa của mảng nên cứ khai báo ReDim dArr(1 To Rows.Count, 1 To 2), và trên máy em chạy không việc gì. Hôm nay chuyển file sang máy khác (Cùng đời office) thì bị lỗi. Mong các anh chị giúp đơ ạ!
Mà đảo mảng qua lại làm gì, lặp một lượt mảng gốc, mỗi dòng lấy ngày cuối trừ ngày đầu, rồi tổng lại thì có bao nhiêu dòng rồi đó
 
Upvote 0
Đây là cách em làm lâu nay khi viết code kiểu này. Em không biết trước số dòng tối đa của mảng nên cứ khai báo ReDim dArr(1 To Rows.Count, 1 To 2), và trên máy em chạy không việc gì. Hôm nay chuyển file sang máy khác (Cùng đời office) thì bị lỗi. Mong các anh chị giúp đơ ạ!
.
Nhập công thức này vào F1 chẳng hạn, rồi lấy giá trị của nó để khai báo mảng, Bỏ vòng lặp thứ hai trong code của bạn:

=SUMPRODUCT(C2:C30-B2:B30+(C2:C30>0))

Như dữ liệu trong file là 27812 dòng
 
Upvote 0
Mà đảo mảng qua lại làm gì, lặp một lượt mảng gốc, mỗi dòng lấy ngày cuối trừ ngày đầu, rồi tổng lại thì có bao nhiêu dòng rồi đó
Tính vầy là thiếu rồi, số thiếu đúng bằng số dòng của mảng gốc. :D
--
Có thể dùng Evaluate tính trực tiếp trong code như vầy:
Mã:
sodong = Evaluate(Replace(Replace("sum(sh!c2:c99-sh!b2:b99+1)", "99", lastRow), "sh", "'" & Sheet1.Name & "'"))
 
Upvote 0
Tính vầy là thiếu rồi, số thiếu đúng bằng số dòng của mảng gốc. :D
--
Có thể dùng Evaluate tính trực tiếp trong code như vầy:
Mã:
sodong = Evaluate(Replace(Replace("sum(sh!c2:c99-sh!b2:b99+1)", "99", lastRow), "sh", "'" & Sheet1.Name & "'"))
Nhờ Anh chỉ giúp bẫy lỗi đối với những dạng bài toán này với ạ! Vì code ở trên là em chỉ ví dụ trường hợp mảng động thôi ạ
 
Upvote 0
Tính vầy là thiếu rồi, số thiếu đúng bằng số dòng của mảng gốc. :D
--
Có thể dùng Evaluate tính trực tiếp trong code như vầy:
Mã:
sodong = Evaluate(Replace(Replace("sum(sh!c2:c99-sh!b2:b99+1)", "99", lastRow), "sh", "'" & Sheet1.Name & "'"))
Bộp chộp thiếu mất +1 anh ạ!
 
Upvote 0
Nếu là mình thì thay vì
PHP:
  ReDim dArr(1 To Rows.Count, 1 To 2)
    lastRow = Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
Mình sẽ đi xác định
Mã:
    lastRow = Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
sau đó mới xài

ReDim dArr(1 To lastRow, 1 To 2)
 
Upvote 0
Nhờ Anh chỉ giúp bẫy lỗi đối với những dạng bài toán này với ạ! Vì code ở trên là em chỉ ví dụ trường hợp mảng động thôi ạ
Tôi làm cho bạn 2 ví dụ.
Sub Test1 sẽ thử khai báo mảng với 1048576 dòng, nếu lỗi thì khai báo với 65536 dòng, xử lý lỗi ngay trong sub.
Sub Test1 dùng một hàm phụ để khai báo mảng, nếu lỗi sẽ giảm dần chỉ số dòng đến khi có thể khai báo được.
Mã:
Sub Test1()
    On Error GoTo XyLyLoi
    Dim Arr As Variant
10  ReDim Arr(1 To 1048576, 1 To 500)
    MsgBox "Mang duoc khai bao voi kich thuoc " & UBound(Arr, 1) & "x500"
Exit Sub
XyLyLoi:
    If Erl = 10 Then
        On Error GoTo 0
        ReDim Arr(1 To 65536, 1 To 500)
        Resume Next
    Else
        On Error GoTo 0
        Resume
    End If
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub Test2()
    Dim Arr As Variant
    RedimArray Arr, 10 ^ 6, 500
    MsgBox "Mang duoc khai bao voi kich thuoc " & UBound(Arr, 1) & "x500"
End Sub
Private Sub RedimArray(ByRef Arr As Variant, ByVal UB1 As Long, ByVal UB2 As Long)
    On Error Resume Next
    Do
        Err.Clear
        ReDim Arr(1 To UB1, 1 To UB2)
        UB1 = UB1 - 50000
    Loop Until Err.Number = 0 Or UB1 < 1
End Sub
 
Upvote 0
Tôi làm cho bạn 2 ví dụ.
Sub Test1 sẽ thử khai báo mảng với 1048576 dòng, nếu lỗi thì khai báo với 65536 dòng, xử lý lỗi ngay trong sub.
Sub Test1 dùng một hàm phụ để khai báo mảng, nếu lỗi sẽ giảm dần chỉ số dòng đến khi có thể khai báo được.
Mã:
Sub Test1()
    On Error GoTo XyLyLoi
    Dim Arr As Variant
10  ReDim Arr(1 To 1048576, 1 To 500)
    MsgBox "Mang duoc khai bao voi kich thuoc " & UBound(Arr, 1) & "x500"
Exit Sub
XyLyLoi:
    If Erl = 10 Then
        On Error GoTo 0
        ReDim Arr(1 To 65536, 1 To 500)
        Resume Next
    Else
        On Error GoTo 0
        Resume
    End If
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub Test2()
    Dim Arr As Variant
    RedimArray Arr, 10 ^ 6, 500
    MsgBox "Mang duoc khai bao voi kich thuoc " & UBound(Arr, 1) & "x500"
End Sub
Private Sub RedimArray(ByRef Arr As Variant, ByVal UB1 As Long, ByVal UB2 As Long)
    On Error Resume Next
    Do
        Err.Clear
        ReDim Arr(1 To UB1, 1 To UB2)
        UB1 = UB1 - 50000
    Loop Until Err.Number = 0 Or UB1 < 1
End Sub
Cảm ơn Anh nhiều ạ! Em đang có hướng xử lý thế này có được không ạ:

Mã:
Function DongCuoiMang(col As Long) As Long
Dim dArr, sArr(), s
On Error Resume Next
sArr = Array(Rows.Count, 500000, 200000, 100000, 65536)
    For Each s In sArr
        Err.Clear
        ReDim dArr(1 To s, 1 To col)
        If Err.Number = 0 Then
            DongCuoiMang = s
            Exit For
        End If
    Next s
End Function
 
Upvote 0
Cảm ơn Anh nhiều ạ! Em đang có hướng xử lý thế này có được không ạ:

Mã:
Function DongCuoiMang(col As Long) As Long
Dim dArr, sArr(), s
On Error Resume Next
sArr = Array(Rows.Count, 500000, 200000, 100000, 65536)
    For Each s In sArr
        Err.Clear
        ReDim dArr(1 To s, 1 To col)
        If Err.Number = 0 Then
            DongCuoiMang = s
            Exit For
        End If
    Next s
End Function
Đã khai báo được rồi thì dùng mang đó luôn (như tôi làm ở sub Test2) chứ lấy chỉ số dòng làm gì, rồi lại Redim thêm một lần nữa à?
Ngoài ra cũng nói thêm do bạn hỏi cách bẫy lỗi nên tôi ví dụ như vậy nhưng theo tôi không nên làm như vậy. Tốt nhất là chỉ khai báo số dòng vừa đủ dùng, kể cả trong trường hợp phải thêm một vòng lặp để xác định kích thước mảng nếu việc xác định kích thước mảng không phức tạp.
 
Upvote 0
Web KT
Back
Top Bottom