[[20210716123224]] 『メモリーの解放方法・タイミング』(タム) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『メモリーの解放方法・タイミング』(タム)

初歩的な質問ですが、メモリの開放についてご質問があります

以下の2つはメモリの開放ができるとのことですが、作成した関数で
End function を行った場合もメモリの開放は行われるのでしょうか?

大変申し訳ございませがどなたかご教示お願いします。

?@ Set a = Nothing
?A End Sub

< 使用 Excel:Office365、使用 OS:Windows10 >


VBA オブジェクトとメモリの関係 〜 オブジェクトが参照型と呼ばれる理由 - t-hom’s diary
https://thom.hateblo.jp/entry/2016/10/06/202616

↑より抜粋

 >Nothingをセットするとオブジェクトが破棄されると教えられた人も居るだろう。でもその理解は正しくない。
 >Set c = Nothingとすると変数cからオブジェクトへの参照が無くなるだけで、dからの参照は有効なままである。

 >オブジェクトが破棄されるのは、そのオブジェクトがどこからも参照されなくなった時である。
 >プロシージャが終了するとローカル変数はすべて破棄される

 >オブジェクトへの参照が0になった時点で破棄される管理方式を、参照カウント方式と呼ぶ

 >参照カウント方式というのは一つ弱点があって、オブジェクト同士で循環参照を作ってしまうと
 >参照カウントが0にならずにいつまでもメモリに残ってしまう。

(白茶) 2021/07/16(金) 13:15


(白茶) 様
ご教示ありがとうございました。
(タム) 2021/07/16(金) 14:08

 理解しちゃったんですか。。すごいなぁ。

 このテーマは、常に不完全燃焼気味なんですよねぇ。。私は。

 それにしても、白茶さんが引用された記事は、今までで一番よく書けている印象があります。

 それを踏まえて便乗質問ですが、以下の理解でいいでしょうか?

 1.ローカルオブジェクト変数について、終了直前にSet Nothingするのは
     メモリ解放の観点からは、無益なコーディングである。

 2.エクセルのオブジェクト、例えばRangeオブジェクトを作成した場合、
   当該オブジェクトは大抵Parentをメンバーに持ちますが、
   これも循環参照と呼ぶのでしょうか?

(半平太) 2021/07/16(金) 17:02


(半平太)さん。
引用元サイトの筆者のthomです。
アクセスログの流入元リンクからこちらのスレッドを見つけたので回答します。

1. 私はそう考えていますが、DBアクセス等で、
  Nothingしたときとそうでないときに差があるという主張を見たことはあります。
  根拠が不明ですし、経験したことはないので私の考えは変わりませんが。

2. RangeオブジェクトはParentを持ちますが、ParentがRangeをChildとして参照しているわけではなく、Rangeは都度作られます。以下、検証用サンプルです。

Sub シートは同じオブジェクト_Rangeは都度生成()

    Dim p1 As Worksheet, p2 As Worksheet
    Set p1 = ThisWorkbook.Sheets(1).Range("A1").Parent
    Set p2 = ThisWorkbook.Sheets(1).Range("A1").Parent

    Debug.Print p1 Is p2 ' Trueになる。

    Debug.Print p1.Range("A1") Is p2.Range("A1") 'Falseになる。
    Debug.Print p1.Range("A1") Is p1.Range("A1") '同じp1の子同士でもFalseになる。
End Sub

以下、参考記事です。
https://thom.hateblo.jp/entry/2017/04/16/021320

たまたま気まぐれでリンクたどってきたので追加の質問には回答できないかと思いますが、参考になれば幸いです。
(thom) 2021/07/16(金) 18:25


 (わ! 筆者さん御登場!)
 解り易かったので引用させて頂きました^^;

 全く自分の言葉が入ってないままなのもどうかと思いますので、
 ちょっと私の考えも書かせて頂きます。

 >メモリ解放の観点からは、無益なコーディング
 たちまち、そういう事になるのかな(たぶん)と思ってます。
 まぁ、Static宣言しているローカルオブジェクト変数は当然破棄されちゃ困るですよね^^;

 私自身NothingのSetって「破棄」というよりは「接続を切る」というイメージで使ってます。
 そもそもメモリの解放とは関係ないんじゃないかな。と。
 なので、
  「メモリを解放する」という「つもり」で行ってるなら×
  「解放される可能性を上げておく」という「つもり」なら○
 感覚的にはそんな風に思ってます。

 「どこからも参照されなくなった時」っていうのがEnd Subのタイミングで合ってるのかが
 正直わかんないんですよね。
 一応、ローカル変数についてはその認識ではいるのですが、
 広域変数になってくると、いったい誰が参照しているのかが分からないのです。

 Excelのプロセスが終了するまでは破棄されない様なパターンも、中にはあるんじゃないのかな?
 と根拠なく想像してます。

 >Parentをメンバーに持ちますが、これも循環参照と呼ぶのでしょうか?
 ちょっとRangeは特殊ですよね。Range以外で考えた方がいいかも。
 (thomさんのコードをRange以外で実行すると↓こうなっちゃう)
    Sub シートとブックの関係なら()
        Dim p1 As Workbook, p2 As Workbook
        Set p1 = ThisWorkbook.Sheets(1).Parent
        Set p2 = ThisWorkbook.Sheets(1).Parent

        Debug.Print p1 Is p2 ' True

        Debug.Print p1.Sheets(1) Is p2.Sheets(1) 'True
        Debug.Print p1.Sheets(1) Is p1.Sheets(1) 'True
    End Sub

 Parent自体はプロパティプロシージャの様なモノであろうと想像しています。
 オブジェクトがメモリに存在すると同時にParentの実態もメモリに在る訳ではなく、
 プロパティを呼び出した時に参照し、End Propertyで切断されているのではないかと。

 変数に格納したもの同士でないと循環参照は成り立たないのではないでしょうか?

(白茶) 2021/07/16(金) 19:03


 thomさん

 こんばんは
 直々にご回答いただけるとはちょっとうれしい気分です。

 Rangeオブジェクトを例にしたのがそもそも悪かったのかなぁと言う気がしています。

 白茶さん

 こんばんは、

 >  「メモリを解放する」という「つもり」で行ってるなら×
 >  「解放される可能性を上げておく」という「つもり」なら○
 > 感覚的にはそんな風に思ってます。

 そこはちっと腑に落ちないです。

 End Subでも、解放される可能性は同じ様に上がるので、コーディングとしては無駄と私は思います。
 ローカル変数の有効期間がそこで終わるのは誰でも知っている事ですから、わざわざやることじゃないです。

 > Parent自体はプロパティプロシージャの様なモノであろうと想像しています。
 そうかも知れませんが、結局なんらかのオブジェクトを経由してParentに辿り着くんですから、
 相互参照じゃなくても、ロングジャーニーの循環参照になってないですかねぇ。

 なんかエクセルは循環参照の蜘蛛の巣である、と言う気になって来ました。
 だいたい、WorkBookなんてなくなることは先ずないので、
 循環参照でメモリが解放されないと言っても誰も困らないですから、
 目くじらを立てることもない。(のかな?)

(半平太) 2021/07/16(金) 22:19


 半平太さん、改めまして、いつも勉強させて貰ってます。

 (まずは一応弁解しとこ^^;)
 >  「メモリを解放する」という「つもり」で行ってるなら×
 >  「解放される可能性を上げておく」という「つもり」なら○
 > 感覚的にはそんな風に思ってます。

 ↑の○×は「書く人のアタマの中の理解として」って意味です。(「つもり」を強調したのはその為)

 NothingのSetで「解放される」と思ってやってる
   → End Subで解放されると思ってない。って事になります。

 「解放される可能性を上げておく」の方は、最後にNothingのSetを書きたい人の気持ちになって捻り出した表現です^^;
 少なくともNothingのSetで「解放される」とは思ってないハズだから、
 >わざわざやることじゃないです。
 は、私もそう思ってるんですが、まあ「書きたければどうぞ」的な意味の○でした。

 >相互参照じゃなくても、ロングジャーニーの循環参照になってないですかねぇ。
 あー、そういう意味ですか。
 うーん...。
 どうなんでしょ...?

 >なんらかのオブジェクトを経由してParentに辿り着く
 とりあえずこれをやっている間は一方通行に見えます。

 Parentを経由して取得したオブジェクトを変数にSetしたとして、
 今度はその変数が参照しているオブジェクト(Parentが表すもの)自身に対して、
 何らかの手段で自分を参照しているオブジェクトをメンバとしてAddしないと
 循環が生じないと思うんですけど、
 エクセルのオブジェクトに関して、そういう事ができるものって何かあるかなぁ..と。
 (イメージ的には↓こんな感じで)
    Set p1 = ThisWorkbook.Sheets(1).Parent
    Set p2 = ThisWorkbook.Sheets(1).Parent
    p1.Add p2

 (ん? なんか違う気もする...)

(白茶) 2021/07/17(土) 01:28


 白茶さん

 遅い時刻にもかかわらず、コメントありがとうございます。

 私は漠然としたアイデアの段階から、
 その先に進めない頭の構造なのでご迷惑をお掛けします。

 具体例として、「人」Classを考えてみます。

 Sub test() ’標準モジュール
     Dim 親 As New 人
     Dim 子 As New 人

     親.姓 = "渋沢"
     親.名 = "栄一"

     子.姓 = "山田"
     子.名 = "太郎"

     親.養子縁組 子

     Debug.Print 子.姓 & 子.名, 子.親.姓 & 子.親.名 '→渋沢太郎  渋沢栄一
 End Sub

 ’「人」Class
 Public 姓 As String
 Public 名 As String
 Public 旧姓 As String
 Public 親 As 人
 Private 子 As 人

 Public Sub 養子縁組(newChild As 人)
     Set 子 = newChild
     Set 子.親 = Me

     '子の苗字を変更
     子.旧姓 = 子.姓
     子.姓 = Me.姓
 End Sub

 上記Test実行後、親と子はメモリ解放がなされるのかどうか?
 ※親は子オブジェクトを持っており、子は親オブジェクトをメンバーに持っている(いた?)。

 解放されない場合、Testプロシージャ側では解放の術はなく(Set 〇=Nothingは無意味)、
 人クラス側のTerminateイベントで、参照を解除するしかないと考えられるが、この推論は正しい?

(半平太) 2021/07/17(土) 17:30


 thomさんのストレステスト(と言っても、10000回ですが)では、

 Terminateイベントで親と子オブジェクトにSet Nothingを入れるのと
 Terminateイベント無しを比較してみましたが、顕著な違いは無かったです。

 どんどん膨らみ、実行が終わって数秒経つと、元のメモリー状態に戻ります。

(半平太) 2021/07/17(土) 18:07


 >Testプロシージャ側では解放の術はなく
 >実行が終わって数秒経つと、元のメモリー状態に戻ります。
  ↑
 嘘言っちゃった。m(__)m
  「人」クラスを手抜き過ぎました。
   メモリーを元に戻すには、VBEのリセットボタン(■)をクリックする必要がありました。

 改めて、以下のコードで、シミュレーションしてみましたが、結果は既述したことと変わらなかったです。
 いずれも、メモリ使用が、50MBから70MBに膨らみます。

 原因は何でしょうか?
  1.参照カウント方式といってもすぐには解放しない。
  2.下のコードではまだ参照カウンターが0に戻っていない。
 そのどっちかなんですかね?

 ’「人」クラス---------
 Public 姓 As String
 Public 名 As String
 Public 旧姓 As String

 Private 親 As 人
 Private 子 As 人

 Public Sub 養子縁組(newChild As 人)

     Set 子 = newChild  '子のオブジェクトを取得
     子.親設定 Me

     '子の苗字を変更
     子.旧姓 = 子.姓
     子.姓 = Me.姓
 End Sub

 Public Sub 親設定(newParent As 人)
     Set 親 = newParent
 End Sub

 Public Function 自己姓名()
     自己姓名 = 姓 & 名
 End Function

 Public Function 親姓名()
     親姓名 = 親.自己姓名
 End Function

 Public Function 子姓名()
     子姓名 = 子.自己姓名
 End Function

 'Terminateイベントで親・子オブジェクトにNothingをセットする場合、コメントを外す
 'Private Sub Class_Terminate()
 '    Set 親 = Nothing
 '    Set 子 = Nothing
 'End Sub

 ’標準モジュール-----------------
 Sub simulation()
     stressTest = True

     Dim i As Long
     For i = 1 To 50000
         Call test
     Next i

     stressTest = False
 End Sub

 Sub test()
     Dim 養親 As New 人
     Dim 養子 As New 人

     養親.姓 = "渋沢"
     養親.名 = "栄一"

     養子.姓 = "山田"
     養子.名 = "太郎"

     養親.養子縁組 養子

     If stressTest = False Then
             Debug.Print 養子.自己姓名, 養子.親姓名, 養子.旧姓  '→渋沢太郎  渋沢栄一 山田
             Debug.Print 養親.自己姓名, 養親.子姓名             '→渋沢栄一 渋沢太郎
     End If

 '    Set 養親 = Nothing
 '    Set 養子 = Nothing

 End Sub

(半平太) 2021/07/18(日) 09:07


おはようございます。

クラスを先週から...ちょこっと勉強している、VBA初心者の(あみな)です。
Set 変数 = Nothing の必要性があるのかとても気になります。

昨夜から、thomさんクラスのコードや、(半平太)さんのここに記載されたコードを
実行して、自分なりに勉強させていただいております。

PCが、重たくなるのは、勘弁です。なので頑張って理解しようとしていますが
まだ、ちんぷんかんぷんです。笑

(半平太)さん、すいませんが
thomさんのストレステスト(10000回)のURLを、教えていただけないでしょうか?
探したのですが、どれか特定ができませんでした。
お時間あるときで大丈夫です、教えていただけたら嬉しいです。

P.S (白茶)さん、先日はご教示いただきありがとうございました。

(thom)さんの、VBA オブジェクトとメモリの関係 〜
オブジェクトが参照型と呼ばれる理由下記の記事は、詳しく記載していただいてますね。
とても勉強になりました。ありがとうございます。
https://thom.hateblo.jp/entry/2016/10/06/202616

(あみな) 2021/07/18(日) 10:42


thomです。続きが気になって立ち寄りました。

半平太さん
Class同士で相互参照された場合は、メインコードの変数からの参照カウントが切れてもオブジェクトが破棄されない為、そもそもClass_Terminateが実行されません。

よって、明示的に参照解除させる命令を作ってメインコードから処理してあげないといけないというのが回答です。

ストレステストにはループさせなくても、Byte型配列を用いるのがおススメです。
以下は10MBを持たせてますので、縁組解除を実行しない場合は、1回実行ごとに概ね20MB増える計算です。以下のコードでは縁組解除を実行しているので、メモリは増えないはずです。(実行オーバーヘッドによる微増は考えられますが。)

'標準モジュール
Sub test()

    Dim 親 As New 人
    Dim 子 As New 人
    親.養子縁組 子
    親.縁組解除
End Sub

'人クラス
Private mem(1024& * 1024& * 10) As Byte
Public 親 As 人
Private 子 As 人
Public Sub 養子縁組(newChild As 人)

     Set 子 = newChild
     Set 子.親 = Me
End Sub

Public Sub 縁組解除()

    Set 子.親 = Nothing
End Sub

また、実行停止ボタンでメモリが開放されるのは、ユーザーによって作成されたオブジェクトの場合はVBAが責任をもって廃棄してくれるからだと思います。

外部参照オブジェクトはそうではないので、たとえばVBAライブラリのCollectionオブジェクトを用いた私のストレステストの場合、実行停止ボタンでもメモリが開放されません。

あみなさん
貼っていただいたURL内で、「次に、循環参照させてみる。」を検索いただいて、その直下のコードのことだと思います。Excelを終了させるまでメモリが開放されないのでご注意ください。

Set 変数 = Nothingは、循環参照を断ち切る用途で使います。一方で標準モジュールの最後に呪文のように書くことには意味はないと考えています。
(thom) 2021/07/18(日) 12:12


DoEvents とかかましたら、どうなりますかね?
結果だけ興味があります。
断ち切りたいのが本音。
(暑すぎ) 2021/07/18(日) 12:17

こんにちは、

thomさん、ありがとうございました。(10000回)ではなく、100000回でしたね。^^;

下記のコードは昨夜実行しました。

 For i = 1 To 100000
  Call 循環参照
 Next

実行前のメモリ使用量は約20メガバイト。
実行後は、、約50メガバイト!
もう一度実行してみると、、約80メガバイト!

同じような結果を確認できました。

>Set 変数 = Nothingは、循環参照を断ち切る用途で使います。一方で標準モジュールの最後に呪文のように書くことには意味はないと考えています。(thom)さん

あ、標準モジュールでは意味がないのですね。なんとなく理解できました。
↓こっちの記事のが初心者には理解しやすいかも^^

VBA Set Obj = Nothing は必要か?
https://thom.hateblo.jp/entry/2015/12/20/135035

では、クラスの使用時に、標準モジュールには、もうSet 変数 = Nothingは
書きませ〜ん。

(あみな) 2021/07/18(日) 13:14


Set 変数 = Nothing の実験〜!!

VBA初心者はコードを実行しないと、覚えないのでこの場をお借りして…便乗質問させてください。
↓こんなコードで、Nothing を入れたらどうなるの?…

 ▼ThisWorkbook

 Private Sub Workbook_Open() 'オブジェクトを生成し、対象シートを設定								
    Dim i As Long								
    For i = 1 To 2								
        Set ShEv(i) = New EventGet								
        Set ShEv(i).TargetSheet = ThisWorkbook.Sheets(i)								
    Next								
 End Sub								

 ▼標準モジュール

 Public ShEv(1 To 2) As EventGet '共通イベント処理クラス								

 ▼クラスモジュール(オブジェクト名 : EventGet)

 Private WithEvents TargetSh As Worksheet								
 Public Property Set TargetSheet(ByVal vObject As Worksheet)								
    Set TargetSh = vObject '対象とするシートをセット								
 End Property								
 Private Sub TargetSh_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean) 'イベント処理								

    Dim myDataRow As Long								
    Dim i As Long								
    myDataRow = 2								

    If ActiveCell.Row >= myDataRow Then								
        Rows(myDataRow & ":" & Rows.Count).Font.ColorIndex = 1								
        i = Target.Row								
        Rows(i).Font.ColorIndex = 3								
    End If								

    Set TargetSh = Nothing ' 各Sheetでイベント処理は1回のみ、コメントアウトでイベント継続可								
    Debug.Print Target ' Sheet1とSheet2のクリックしたセルの文字列が入る。								
 End Sub								

面白いですね。各Sheetで1回ずつ処理したら、強制終了みたいなww
ん?もしやこれは?Kill の替わりに、Nothing は使えるのでしょうか?

すいません。…あんぽんでした。お邪魔しました。m(*T▽T*)m

(あみな) 2021/07/18(日) 14:11


 暑すぎさん へ

 >DoEvents とかかましたら、どうなりますかね?
 1000回に1回入れてみましたが、既述の結果から変化なしです。
 ついでにSleep100も入れてみましたが、効果なかったです。

 thomさん へ

 お立ち寄り頂き、ありがとうございます。

 >Class同士で相互参照された場合は、メインコードの変数からの参照カウントが切れてもオブジェクトが破棄されない為、
 >そもそもClass_Terminateが実行されません。

 なるほどです。言われないと分からない自分が情けない。

 そうなると、循環参照させる積りでクラスを作っている場合、
 循環参照解消メソッドを当該クラスに書いておく必要がある、と言うことになりますね。
 こんな話、いままで聞いたことなかったです。

 ありがとうございました。私の便乗質問は以上で解決とします。

(半平太) 2021/07/18(日) 15:18


 あみな さん

 普通は、ThisWorkbookの Workbook_SheetBeforeDoubleClickイベントを使うと思うのですが、
 わざわざEventGetクラスを作る意図は何ですか?

 処理回数を限定したい、と言う事なんですか?
 そうだとした場合、処理カウンタを持たせるだけで済みそうな気がするんですが・・

(半平太) 2021/07/18(日) 15:25


スタックオーバーフローにDoEventsが有効なのでもしやと思いまたが
残念な結果でしたね、計測ありがとうございました。
まあ、私は入れたい派なので入れますけど。
(暑すぎ) 2021/07/18(日) 15:52

(半平太)さん、コメントありがとうございます。

>処理回数を限定したい、と言う事なんですか?
そうだとした場合、処理カウンタを持たせるだけで済みそうな気がするんですが・・

あ、えと… Set 変数 = Nothing の実験でして、ちゃんと変数が消えるかなと言う
程度でございます。すいません。(_ _。)

>普通は、ThisWorkbookの Workbook_SheetBeforeDoubleClickイベントを使うと思うのですが、
わざわざEventGetクラスを作る意図は何ですか?

EventGetクラスを作る意図は何ですか?と言われると…こんな返信でいいのか
解りませんが、Withevents を使用して動けば何でも良かったんです。^^;

上にも書きましたが、クラスの勉強を始めたのが先週からでして
VBAさえも私くし、今年に入ってからした、ヨチヨチ制でありまして

クラスの Class_Initialize [ コンストラクタ ] と Class_Terminate [ デストラクタ ] の
使い方は、なんとな〜く理解したので、次に最低限抑えておきたいイベントで
Witheventsだけはと思い、内容はなんでも好かったんですが下記のサイトを参考に
してやってみました。

3.4.3 共通イベント処理クラス
https://sites.google.com/site/compositiosystemae/home/vbaworld/middle/classdeverop/commonevent

あ、それと
Workbook_SheetBeforeDoubleClick イベントで特定シートを指定する方法の
概念が私くしの中でありませんでした。勝手に、全シートでするイベントだと思っておりました。

Worksheet_BeforeDoubleClick に関する記載のHPは多いと思うのですが
Workbook_SheetBeforeDoubleClick で特定のシートだけにイベントを発生させる方法が
詳しく記載されてるHPが、過去に検索が出来なかったと言うのもあります。

Worksheet_BeforeDoubleClick のコードを、シートモジュールにコピペして
各シート毎にするものなんだぐらいの認識でございました。^^;

あ、それと
UserFormが私くし、割と好きでして...UserFormのコントロールで
Witheventsを使用してコードで作成されたコントロールにイベント処理
を作成する方法を今からちょうど勉強しようと思っておりまして
下記のサイトにちょっと面白そうな、Withevents の記事がありまして…

VBAの部品庫さん
Witheventsキーワードを使用すると、変数オブジェクトでイベントの作成が可能になります。最強説があります
https://vbanobuhinko.com/withevents%e3%81%ab%e3%81%a4%e3%81%84%e3%81%a6/

で…Withevents に、少々こだわっていた次第です。
ここまでスクロールして、私くし(あみな)の個人事情をご覧になりました。
閲覧してしまった…皆さま、貴重な皆様の時間を無駄にしてすいませんでした。m(_ _ )m

(あみな) 2021/07/19(月) 12:23


 質問が何なのか分からなくなっちゃった。^^;
 大したことでもなさそうな雰囲気ありありですね。

 もし切実な悩みなら、すみませんがもう一度質問内容をクリアに書いてください。
 そうじゃなければ、この私の書き込みに対する返信は不要です。

(半平太) 2021/07/19(月) 13:43


 だいぶ置いてけぼりを食らってしまいました...  (これはもう読み返すだけでひと苦労^^;)

 もう終わってるっぽいですよね。
 たいへん興味深い内容なので、がんばって読み返して追い付こうと思います。

 ひとつ、
 今回のテーマである「メモリの解放」とは直接関係ない無駄話なのですが、
 「呪文のようにNothingをSet」する事の無益性から派生して、
 なんかソコだけが独り歩きしてしまわないかという老婆心が働いてしまいました。

 少なくとも今回このトピにご参加の方々には無用な心配とは承知しておりますが、
 あまりにも大雑把に「後始末の省略」という意味に拡大解釈されてしまうと、
 初歩的なところで「うっかり」をやっちゃう事も考えられます。

    '[clsMouseEv]モジュール(クラスモジュール) ==========================================================================
    Option Explicit
    Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As apiCursorPos) As Long
    Private Declare PtrSafe Sub Sleep Lib "KERNEL32.dll" (ByVal dwMilliseconds As Long)
    Private Type apiCursorPos
        X As Long
        Y As Long
    End Type
    Private LoopFlg As Boolean, aCurPos As apiCursorPos
    Public Event MouseMove(X As Long, Y As Long)

    Public Property Get Switch() As Boolean
        Switch = LoopFlg
    End Property
    Public Property Let Switch(newBool As Boolean)
        LoopFlg = newBool
        If LoopFlg Then Call Observer
    End Property
    Private Sub Observer()
        Dim tmpCurPos As apiCursorPos
        Do
            If Not LoopFlg Then Exit Do
            tmpCurPos = aCurPos
            GetCursorPos aCurPos
            If tmpCurPos.X <> aCurPos.X Or tmpCurPos.Y <> aCurPos.Y Then RaiseEvent MouseMove(aCurPos.X, aCurPos.Y)
            DoEvents
            Sleep 10
        Loop
    End Sub
    Private Sub Class_Terminate()
        Debug.Print "Class_Terminate"
    End Sub

    '[UserForm1]モジュール =============================================================================================
    Option Explicit
    Private WithEvents c As clsMouseEv
    Private Sub c_MouseMove(X As Long, Y As Long)
        Me.Caption = X & "/" & Y
    End Sub
    Private Sub UserForm_Click()
        c.Switch = Not c.Switch
    End Sub
    Private Sub UserForm_Initialize()
        Set c = New clsMouseEv
    End Sub
    Private Sub UserForm_Terminate()
        Debug.Print "UserForm_Terminate"
        c.Switch = False '←コレをせずにUserFormを[×]で閉じるとclsMouseEv内のループが終わらない(Set c = Nothingでも同じ)
    End Sub

 UserFormを[×]で終了すれば、その中で作ったcも勝手に破棄されるという拡大解釈です。
 ループ回してる間はcは破棄されません。
 cにNothingをSetしても終わらないです。ループが終わらないとSetされないから。
 (そもそもVBEウインドウ見れば[実行中]のままになってるハズ)

 「うっかり」具合だけで言うと、
 TextStreamをOpenしてCloseせずに終わっちゃうのと同レベルの「うっかり」具合でしょうか^^;
 (自分で無限ループ回しておきながら、それ止めなかった...っていううっかり)

 まぁ上記は極端な例(クラスにしなくても出来ることをわざとクラスにしてるからこそ発生する事故)ですけど、
 これも違う意味で「呪文のようにNothingをSetしても意味がない」という事例になり得るかなぁと...

 「呪文のように」という部分がミソですね。

(白茶) 2021/07/19(月) 21:10


コーティングでカバーしろと言われればそれまでなんですけど
もしメモリ開放できていなかった場合強制的に開放する方法(アプリケーションを閉じたり■ボタンを押す以外に)ってあるんですかね?
エンドユーザーに悟られないように開放したいんですけど

↓だと解放されない
Class1
Private mem(1024& * 1024& * 10) As Byte
Private c As Class1
Public Sub Add(ByVal newClass As Class1)

    Set c = newClass
End Sub
Private Sub Class_Terminate()
    Set c = Nothing
End Sub

Module1
Sub test()

    Dim p As New Class1
    p.Add p
End Sub
Sub sample()
    Dim i As Long
    For i = 1 To 10
        Call test
    Next i
End Sub

(モゲラ) 2021/07/20(火) 10:45


 いろいろついていけてないですが、
 >強制的に開放する方法
 これは
 End 
 すればいいのでは?
(´・ω・`) 2021/07/20(火) 11:34

End SubじゃなくEndでぬけたら開放するんですか・・・
それは気づかなかった・・・
(モゲラ) 2021/07/20(火) 11:40

 白茶さん 

 興味深いサンプルをご提示いただき、ありがとうございます。

 うっかりはプログラミングに付き物と思っているんですが、
 この種のものは、気づくことも難しいので厄介ですね。

 実務家は何か対策を編み出しているんですかねぇ。。

 循環参照対策と同様、PepareForTermination メソッドを必ず書くルールにして、
 オブジェクトを破棄したいときは先ずそれをCallする、 なんてことになるのかなぁ。

 まぁ、私は実務家じゃないので、他人事的な発想ですけども。(=自分事としての現実味がない)

(半平太) 2021/07/20(火) 12:43


 実験報告です

 >VBAライブラリのCollectionオブジェクトを用いた私のストレステストの場合、
 >実行停止ボタンでもメモリが開放されません。

 >End SubじゃなくEndでぬけたら開放するんですか・・・

 上の二つの関係について、thomさんのストレステストをやったところ、
 Endステートメントでもメモリは解放されなかったです。

(半平太) 2021/07/21(水) 10:48


人クラスのは解放されますけどCollectionにCollection入れるのでは解放されてないですね・・・
人クラス循環してる?してない?
Sub Sample()
    Dim i As Long
    For i = 1 To 100000
        Call 循環参照
    Next i
    End     '<--解放されない
End Sub

Sub 循環参照()

    Dim c As Collection
    Set c = New Collection
    Dim d As Collection
    Set d = New Collection
    c.Add d
    d.Add c
End Sub

Sub Sample1()

    Dim i As Long
    For i = 1 To 5
        Call test
    Next i
    End     '<--解放される
End Sub
Sub test()
    Dim 親 As New 人
    Dim 子 As New 人
    親.養子縁組 子
    子.養子縁組 親
End Sub

'人クラス
Private mem(1024& * 1024& * 10) As Byte
Public 親 As 人
Private 子 As 人
Public Sub 養子縁組(newChild As 人)

     Set 子 = newChild
     Set 子.親 = Me
End Sub
Public Sub 縁組解除()
    Set 子.親 = Nothing
End Sub

(モゲラ) 2021/07/21(水) 12:01


σ(・ω・*)んと

ウー(´-ω-`)ーン...Collection...オブジェクトの関連は、難しい。

退散εεεε (∀`*ゞ)エヘヘ

(あみな) 2021/07/21(水) 22:16


 End ステートメントのヘルプによると、

 >End ステートメントを実行すると、
 >すべてのモジュール内のすべてのモジュール レベル変数および静的変数が初期化されます。

 >End ステートメントは、Unload、QueryUnload、および Terminate イベント
 >またはその他の Visual Basic コードが起動しなくても、
 >即時にコードの実行を停止します。

 >クラス モジュールで作成されたオブジェクトは破壊され、
 >Open ステートメントを使って開かれたファイルは閉じられ、
 >プログラムで使われていたメモリは解放されます。
 >他のプログラムが保持しているオブジェクトへの参照は、無効になります。

 う〜ん...
 Endステートメント強烈ですね。
 普段アドインの中で一時的にいろいろメモってる状況の私としては、
 そう手軽に使える手段とは言えない様です。

 UserFormやClassは仕方ないにしても、
 ADOやFSO、直接関係の無い広域変数やStatic変数までも初期化されちゃあ
 もう、Excel再起動してるのとほぼ変わんないですね。

(白茶) 2021/07/24(土) 23:19


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.