函数返回时出现 NullReferenceException

发布于 2024-07-24 21:11:02 字数 979 浏览 9 评论 0原文

我在运行多线程应用程序时收到 NullReferenceException,但仅当我在调试器之外以发布模式运行时才收到。 堆栈跟踪被记录,并且它始终指向相同的函数调用。 我在函数中放置了几个日志语句来尝试确定它会到达多远,并且每个语句都会被记录,包括函数最后一行的语句。 有趣的是,当 NullReferenceException 发生时,函数调用后的语句不会被记录:

    // ...
    logger.Log( "one" );  // logged
    Update( false );
    logger.Log( "eleven" );  // not logged when exception occurs
}

private void Update( bool condition )
{
    logger.Log( "one" );  // logged
    // ...  
    logger.Log( "ten" );  // logged, even when exception occurs
}

该异常并不是每次调用函数时都会发生。 堆栈是否有可能在函数执行之前或期间被损坏,从而导致返回地址丢失,从而导致空引用? 我不认为这种事情在 .NET 下是可能的,但我猜奇怪的事情已经发生了。

我尝试用函数的内容替换对函数的调用,因此一切都发生内联,然后异常发生在如下所示的行上:

foreach ( ClassItem item in classItemCollection )

我已经通过日志记录验证了“classItemCollection”不为空,并且我也尝试将 foreach 更改为 for 以防 IEnumerator 做了一些有趣的事情,但异常发生在同一行。

关于如何进一步调查这个问题有什么想法吗?

更新:一些响应者提出了可能的解决方案,以确保记录器不为空。 需要明确的是,在异常开始发生后添加日志记录语句是为了调试目的。

I am getting a NullReferenceException when running my multi-threaded application, but only when I run in Release mode outside of the debugger. The stack trace gets logged, and it always points to the same function call. I put several logging statements in the function to try to determine how far it would get, and every statement gets logged, including one on the last line of the function. What is interesting is that when the NullReferenceException occurs, the statement after the function call does not get logged:

    // ...
    logger.Log( "one" );  // logged
    Update( false );
    logger.Log( "eleven" );  // not logged when exception occurs
}

private void Update( bool condition )
{
    logger.Log( "one" );  // logged
    // ...  
    logger.Log( "ten" );  // logged, even when exception occurs
}

The exception does not occur every time the function is called. Is it possible that the stack is being corrupted either before or during execution of the function such that the return address is lost, resulting in the null reference? I didn't think that sort of thing was possible under .NET, but I guess stranger things have happened.

I tried replacing the call to the function with the contents of the function, so everything happens inline, and the exception then occurs on a line that looks like this:

foreach ( ClassItem item in classItemCollection )

I have verified through logging that the "classItemCollection" is not null, and I also tried changing the foreach to a for in case the IEnumerator was doing something funny, but the exception occurs on the same line.

Any ideas on how to investigate this further?

Update: Several responders have suggested possible solutions having to do with making sure the logger isn't null. To be clear, the logging statements were added for debugging purposes after the exception started happening.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

¢好甜 2024-07-31 21:11:02

我发现我的空引用。 就像 Fredrik 和 micahtan 所建议的那样,我没有为社区提供足够的信息来找到解决方案,所以我认为我应该发布我发现的内容来解决这个问题。

这是所发生情况的表示:

ISomething something = null;

//...

// the Add method returns a strong reference to an ISomething
// that it creates.  m_object holds a weak reference, so when
// "this" no longer has a strong reference, the ISomething can
// be garbage collected.
something = m_object.Add( index );

// the Update method looks at the ISomethings held by m_object.
// it obtains strong references to any that have been added,
// and puts them in m_collection;
Update( false );

// m_collection should hold the strong reference created by 
// the Update method.
// the null reference exception occurred here
something = m_collection[ index ];

return something;

问题原来是我使用“something”变量作为临时强引用,直到 Update 方法获得永久引用。 编译器在发布模式下优化掉“something = m_object.Add();” 分配,因为“某物”在再次分配之前不会被使用。 这允许 ISomething 被垃圾回收,因此当我尝试访问它时,它不再存在于 m_collection 中。

我所要做的就是确保在调用 Update 之前我持有强引用。

我怀疑这对任何人都有用,但如果有人好奇,我不想让这个问题得不到答案。

I found my null reference. Like Fredrik and micahtan suggested, I didn't provide enough information for the community to find a solution, so I figured I should post what I found just to put this to rest.

This is a representation of what was happening:

ISomething something = null;

//...

// the Add method returns a strong reference to an ISomething
// that it creates.  m_object holds a weak reference, so when
// "this" no longer has a strong reference, the ISomething can
// be garbage collected.
something = m_object.Add( index );

// the Update method looks at the ISomethings held by m_object.
// it obtains strong references to any that have been added,
// and puts them in m_collection;
Update( false );

// m_collection should hold the strong reference created by 
// the Update method.
// the null reference exception occurred here
something = m_collection[ index ];

return something;

The problem turned out to be my use of the "something" variable as a temporary strong reference until the Update method obtained a permanent one. The compiler, in Release mode, optimizes away the "something = m_object.Add();" assignment, since "something" isn't used until it is assigned again. This allowed the ISomething to be garbage collected, so it no longer existed in m_collection when I tried to access it.

All I had to do was ensure that I held a strong reference until after the call to Update.

I am doubtful that this will be of any use to anyone, but in case anyone was curious, I didn't want to leave this question unanswered.

浅浅淡淡 2024-07-31 21:11:02

它记录“十”的事实会让我首先看看:

  • logger 是否曾经被分配过...这是否可能以某种方式变成 null
  • Log 本身内部的错误

很难在没有足够背景的情况下告诉我们 - 但这就是我调查它的方式。 您还可以在某处添加一个简单的 null 测试; 作为一种厚颜无耻的方法,您可以将 Log 方法重命名为其他名称,并添加一个扩展方法:

[Conditional("TRACE")]
public static void Log(this YourLoggerType logger, string message) {
    if(logger==null) {
       throw new ArgumentNullException("logger",
            "logger was null, logging " + message);
    } else {
       try {
           logger.LogCore(message); // the old method
       } catch (Exception ex) {
           throw new InvalidOperationException(
                "logger failed, logging " + message, ex);
       }
    }
}

您现有的代码应该调用新的 Log 扩展方法,并且异常将明确它在哪里呕吐。 也许修复后将其改回来......或者也许保留它。

The fact that it logs "ten" would make me look first at:

  • is logger ever assigned... is this perhaps becoming null somehow
  • is the bug inside Log itself

Hard to tell without enough context for either - but that is how I'd investigate it. You could also add a simple null test somewhere; as a cheeky approach, you could rename the Log method to something else, and add an extension method:

[Conditional("TRACE")]
public static void Log(this YourLoggerType logger, string message) {
    if(logger==null) {
       throw new ArgumentNullException("logger",
            "logger was null, logging " + message);
    } else {
       try {
           logger.LogCore(message); // the old method
       } catch (Exception ex) {
           throw new InvalidOperationException(
                "logger failed, logging " + message, ex);
       }
    }
}

Your existing code should call the new Log extension method, and the exception will make it clear exactly where it barfed. Maybe change it back once fixed... or maybe leave it.

国产ˉ祖宗 2024-07-31 21:11:02

同意弗雷德里克的观点——需要更多细节。 也许可以从一个地方开始寻找:您提到了多线程应用程序以及发布中发生的错误,但没有提到调试。 您可能会遇到多个线程访问相同对象引用的计时问题。

无论如何,我也可能会

Debug.Assert(classItemCollection != null);

在循环迭代之前放置一个:。 它不会在发布模式下帮助您,但如果(何时?)它发生在调试模式下,它可能会帮助您捕获问题。

Agree w/Fredrik -- more details are necessary. One place to maybe start looking: you mention multi-threaded application and the error happening in release but not debug. You might be running into a timing issue with multiple threads accessing the same object references.

Regardless, I'd also probably put a:

Debug.Assert(classItemCollection != null);

right before the loop iteration. It won't help you in release mode, but it may help you catch the problem if (when?) it happens in Debug.

拧巴小姐 2024-07-31 21:11:02

我会寻找将记录器或其依赖项之一设置为空的代码。 记录器是否有属性,当设置为 null 时,可能会触发此问题? 发布模式有时会加速应用程序的执行,这可以揭示被调试模式和/或调试器的性能损失所掩盖的同步问题。

I'd look for code that's setting logger or one of its dependents to null. Are there properties of logger that, when set to null, might trigger this? Release mode sometimes speeds up application execution which can reveal synchronization problems that are masked by the performance penalty of debug mode and/or the debugger.

念三年u 2024-07-31 21:11:02

事实上“11”没有被记录,这让我相信记录器在调用之前被设置为空。 您可以将其包装在 try/catch 中并查看它是否命中块的 catch 部分吗? 也许您可以在发生这种情况时插入 MessageBox.Show 或向已知文件写入一些内容。

The fact that "eleven" isn't getting logged leads me to believe that logger is being set to null just before that call is made. Can you wrap it in a try/catch and see if it hits the catch portion of the block? Maybe you can insert a MessageBox.Show or write something to a known file when that happens.

作业与我同在 2024-07-31 21:11:02

您是否从多个线程修改 classItemCollection? 如果您在另一个线程中更改集合,则可能会使迭代器无效,这可能会导致异常。 您可能需要用锁来保护访问。

编辑:
您可以发布有关 ClassItem 和 classItemCollection 类型的更多信息吗?

另一种可能性是 ClassItem 是值类型,而 classItemCollection 是通用集合,并且以某种方式将 null 添加到集合中。 下面抛出一个 NullReferenceException:

        ArrayList list=new ArrayList();

        list.Add(1);
        list.Add(2);
        list.Add(null);
        list.Add(4);

        foreach (int i in list)
        {
            System.Diagnostics.Debug.WriteLine(i);
        }

这个特殊问题可以通过 int 来解决吗? i 或 Object i 在 foreach 中或使用通用容器。

Are you modifying classItemCollection from multiple threads? If you change the collection in another thread you may be invalidating the iterator which might lead to your exception. You may need to protect access with a lock.

edit:
Can you post more info about the types of ClassItem and classItemCollection?

Another possibility is that ClassItem is a value type and classItemCollection is a generic collection and somehow a null is getting added to the collection. The following throws a NullReferenceException:

        ArrayList list=new ArrayList();

        list.Add(1);
        list.Add(2);
        list.Add(null);
        list.Add(4);

        foreach (int i in list)
        {
            System.Diagnostics.Debug.WriteLine(i);
        }

This particular problem can be resolved by int? i or Object i in the foreach or using a generic container.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文