fyne-io / fyne

Cross platform GUI toolkit in Go inspired by Material Design
https://fyne.io/
Other
25.21k stars 1.4k forks source link

Memory leak in widget.Table due to constantly creating cells to cache.Renderer(impl) while table.Refresh() #4903

Open ZSA233 opened 5 months ago

ZSA233 commented 5 months ago

Checklist

Describe the bug

widget.Table will CreateCell every time when table.Refresh() called. detail below How to reproduce

How to reproduce

  1. testing code

    
    func main() {
    myApp := app.New()
    
    myWindow := myApp.NewWindow("test")
    myWindow.Resize(fyne.NewSize(1000, 500))
    rows := [][]string{}
    for i := 0; i < 100; i++ {
        rows = append(rows, make([]string, 100))
        for j := 0; j < 100; j++ {
            rows[i][j] = fmt.Sprintf("%d:%d", i, j)
        }
    }
    
    table := widget.NewTable(func() (rows int, cols int) {
        return 100, 100
    }, func() fyne.CanvasObject {
        log.Println("Create Cell: ", time.Now().UnixNano())
        return widget.NewLabel("")
    }, func(id widget.TableCellID, object fyne.CanvasObject) {
        l := object.(*widget.Label)
        l.SetText(rows[id.Row][id.Col])
    })
    myWindow.SetContent(table)
    
    go refreshLoop(table)
    myWindow.ShowAndRun()

}

func refreshLoop(table widget.Table) { tick := time.NewTicker(time.Millisecond 10) defer tick.Stop() for _ = range tick.C { table.Refresh() } }

2. you will see the memory increasing and `Create Cell: xxxxx` printing constantly.
3. tracking chain: 
- table.Refresh()
- t.t.templateSize()
- t.CreateCell()
- template.MinSize()
- cache.Renderer(l.super())
- renderers[wid] = rinfo

4. it will allways cache a new `widget.Label` into `renderers`. Consequently, no gc reach.
5. pprof heap tree
```shell
(pprof) tree
Showing nodes accounting for 118.15MB, 97.88% of 120.71MB total
Dropped 26 nodes (cum <= 0.60MB)
Showing top 80 nodes out of 107
----------------------------------------------------------+-------------
      flat  flat%   sum%        cum   cum%   calls calls% + context          
----------------------------------------------------------+-------------
                                           40.51MB   100% |   fyne.io/fyne/v2/widget.(*textRenderer).Refresh
   31.51MB 26.10% 26.10%    40.51MB 33.56%                | fyne.io/fyne/v2/widget.(*RichText).cachedSegmentVisual
                                               9MB 22.22% |   fyne.io/fyne/v2/widget.(*TextSegment).Visual
----------------------------------------------------------+-------------
                                              16MB   100% |   fyne.io/fyne/v2/widget.NewLabel
      16MB 13.26% 39.36%       16MB 13.26%                | fyne.io/fyne/v2/widget.NewLabelWithStyle
----------------------------------------------------------+-------------
                                           17.25MB   100% |   fyne.io/fyne/v2/widget.NewRichTextWithText
      11MB  9.11% 48.48%    17.25MB 14.29%                | fyne.io/fyne/v2/widget.NewRichText
                                            6.25MB 36.22% |   fyne.io/fyne/v2/widget.(*RichText).updateRowBounds
----------------------------------------------------------+-------------
                                            8.85MB   100% |   fyne.io/fyne/v2/internal/painter.loadMeasureFont
    8.85MB  7.33% 55.81%     8.85MB  7.33%                | github.com/go-text/typesetting/opentype/loader.(*Loader).findTableBuffer
----------------------------------------------------------+-------------
                                            8.50MB   100% |   fyne.io/fyne/v2/widget.(*TextSegment).Visual
    8.50MB  7.04% 62.85%     8.50MB  7.04%                | fyne.io/fyne/v2/canvas.NewText
----------------------------------------------------------+-------------
                                            6.50MB   100% |   fyne.io/fyne/v2/widget.(*RichText).CreateRenderer
    6.50MB  5.39% 68.23%     6.50MB  5.39%                | fyne.io/fyne/v2/canvas.NewRectangle
----------------------------------------------------------+-------------
                                           23.75MB   100% |   fyne.io/fyne/v2/widget.(*Label).CreateRenderer
    6.50MB  5.39% 73.62%    23.75MB 19.68%                | fyne.io/fyne/v2/widget.NewRichTextWithText
                                           17.25MB 72.63% |   fyne.io/fyne/v2/widget.NewRichText
----------------------------------------------------------+-------------
                                           92.61MB 97.37% |   fyne.io/fyne/v2/widget.(*Label).MinSize
                                           13.64MB 14.35% |   fyne.io/fyne/v2/widget.(*BaseWidget).MinSize
                                            1.50MB  1.58% |   fyne.io/fyne/v2/widget.(*BaseWidget).Resize
                                            0.50MB  0.53% |   fyne.io/fyne/v2/widget.(*BaseWidget).Refresh
    5.46MB  4.52% 78.14%    95.11MB 78.80%                | fyne.io/fyne/v2/internal/cache.Renderer
                                           88.66MB 93.21% |   fyne.io/fyne/v2/widget.(*Label).CreateRenderer
                                           13.14MB 13.82% |   fyne.io/fyne/v2/widget.(*Table).CreateRenderer
                                               1MB  1.05% |   fyne.io/fyne/v2/internal/cache.(*expiringCache).setAlive
----------------------------------------------------------+-------------
                                           19.64MB   100% |   fyne.io/fyne/v2/widget.(*RichText).updateRowBounds
       5MB  4.14% 82.28%    19.64MB 16.27%                | fyne.io/fyne/v2/widget.(*RichText).updateRowBounds.func1
                                           13.14MB 66.91% |   fyne.io/fyne/v2.MeasureText
                                            1.50MB  7.64% |   fyne.io/fyne/v2/widget.lineBounds
----------------------------------------------------------+-------------
                                            3.19MB   100% |   fyne.io/fyne/v2/internal/async.(*CanvasObjectQueue).Out
    3.19MB  2.65% 84.93%     3.19MB  2.65%                | sync.(*poolChain).pushHead
----------------------------------------------------------+-------------
                                           64.91MB   100% |   fyne.io/fyne/v2/widget.(*Label).CreateRenderer
       3MB  2.49% 87.41%    64.91MB 53.77%                | fyne.io/fyne/v2/widget.(*RichText).CreateRenderer
                                           42.51MB 65.49% |   fyne.io/fyne/v2/widget.(*textRenderer).Refresh
                                           12.90MB 19.87% |   fyne.io/fyne/v2/widget.(*RichText).updateRowBounds
                                            6.50MB 10.02% |   fyne.io/fyne/v2/canvas.NewRectangle
----------------------------------------------------------+-------------
                                               3MB   100% |   fyne.io/fyne/v2/internal/async.(*CanvasObjectQueue).In
       3MB  2.49% 89.90%        3MB  2.49%                | fyne.io/fyne/v2/internal/async.init.func1
----------------------------------------------------------+-------------
                                           42.51MB 92.39% |   fyne.io/fyne/v2/widget.(*RichText).CreateRenderer
                                            3.50MB  7.61% |   fyne.io/fyne/v2/widget.(*BaseWidget).Refresh
       2MB  1.66% 91.56%    46.01MB 38.12%                | fyne.io/fyne/v2/widget.(*textRenderer).Refresh
                                           40.51MB 88.05% |   fyne.io/fyne/v2/widget.(*RichText).cachedSegmentVisual
                                            2.50MB  5.43% |   fyne.io/fyne/v2/widget.(*TextSegment).Update
                                               1MB  2.17% |   fyne.io/fyne/v2/canvas.Refresh
----------------------------------------------------------+-------------
                                            1.69MB   100% |   fyne.io/fyne/v2/internal/painter.loadMeasureFont
    1.69MB  1.40% 92.95%     1.69MB  1.40%                | github.com/go-text/typesetting/opentype/api/font/cff.parseIndexContent
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.lineBounds
    1.50MB  1.24% 94.20%     1.50MB  1.24%                | fyne.io/fyne/v2/widget.splitLines
----------------------------------------------------------+-------------
                                            1.06MB   100% |   fyne.io/fyne/v2/internal/painter.loadMeasureFont
    1.06MB  0.87% 95.07%     1.06MB  0.87%                | github.com/go-text/typesetting/opentype/tables.ParseBaseArray
----------------------------------------------------------+-------------
                                               1MB   100% |   fyne.io/fyne/v2/internal/cache.Renderer
       1MB  0.83% 95.90%        1MB  0.83%                | fyne.io/fyne/v2/internal/cache.(*expiringCache).setAlive
----------------------------------------------------------+-------------
    0.84MB  0.69% 96.59%     0.84MB  0.69%                | github.com/goccy/go-json/internal/decoder.init.0
----------------------------------------------------------+-------------
                                            1.55MB   100% |   fyne.io/fyne/v2/internal/painter.loadMeasureFont
    0.55MB  0.46% 97.05%     1.55MB  1.29%                | github.com/go-text/typesetting/opentype/tables.ParseGlyf
----------------------------------------------------------+-------------
    0.50MB  0.41% 97.47%    13.65MB 11.30%                | main.main
                                           13.14MB 96.33% |   fyne.io/fyne/v2/internal/driver/glfw.(*window).SetContent
----------------------------------------------------------+-------------
                                            1.06MB 67.91% |   fyne.io/fyne/v2/internal/driver.WalkVisibleObjectTree
                                            1.06MB 67.91% |   fyne.io/fyne/v2/internal/driver.walkObjectTree.func1
                                            0.50MB 32.09% |   fyne.io/fyne/v2/internal/driver/common.(*Canvas).Refresh
    0.50MB  0.41% 97.88%     1.56MB  1.29%                | fyne.io/fyne/v2/internal/driver.walkObjectTree
                                            1.06MB 67.91% |   fyne.io/fyne/v2/internal/driver.walkObjectTree.func1
----------------------------------------------------------+-------------
                                            1.50MB   100% |   github.com/go-gl/glfw/v3.3/glfw._Cfunc_glfwSetWindowSize
         0     0% 97.88%     1.50MB  1.24%                | _cgoexp_16379c6f66fe_goWindowSizeCB
                                            1.50MB   100% |   github.com/go-gl/glfw/v3.3/glfw.goWindowSizeCB
----------------------------------------------------------+-------------
                                           13.14MB   100% |   fyne.io/fyne/v2/widget.(*RichText).updateRowBounds.func1
         0     0% 97.88%    13.14MB 10.89%                | fyne.io/fyne/v2.MeasureText
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).RenderedTextSize
----------------------------------------------------------+-------------
                                            2.50MB   100% |   fyne.io/fyne/v2/widget.(*TextSegment).Update
         0     0% 97.88%     2.50MB  2.07%                | fyne.io/fyne/v2/canvas.(*Text).Refresh
                                            2.50MB   100% |   fyne.io/fyne/v2/canvas.Refresh
----------------------------------------------------------+-------------
                                            2.50MB 71.43% |   fyne.io/fyne/v2/canvas.(*Text).Refresh
                                               1MB 28.57% |   fyne.io/fyne/v2/widget.(*textRenderer).Refresh
         0     0% 97.88%     3.50MB  2.90%                | fyne.io/fyne/v2/canvas.Refresh
                                            3.50MB   100% |   fyne.io/fyne/v2/internal/driver/common.(*Canvas).Refresh
----------------------------------------------------------+-------------
                                               3MB   100% |   fyne.io/fyne/v2/internal/driver/common.(*Canvas).Refresh
         0     0% 97.88%        3MB  2.49%                | fyne.io/fyne/v2/internal/async.(*CanvasObjectQueue).In
                                               3MB   100% |   fyne.io/fyne/v2/internal/async.init.func1
----------------------------------------------------------+-------------
                                            3.19MB   100% |   fyne.io/fyne/v2/internal/driver/common.(*Canvas).FreeDirtyTextures
         0     0% 97.88%     3.19MB  2.65%                | fyne.io/fyne/v2/internal/async.(*CanvasObjectQueue).Out
                                            3.19MB   100% |   sync.(*poolChain).pushHead
----------------------------------------------------------+-------------
                                            1.06MB   100% |   fyne.io/fyne/v2/internal/driver/common.(*Canvas).walkTree
         0     0% 97.88%     1.06MB  0.88%                | fyne.io/fyne/v2/internal/driver.WalkVisibleObjectTree
                                            1.06MB   100% |   fyne.io/fyne/v2/internal/driver.walkObjectTree
----------------------------------------------------------+-------------
                                            1.06MB   100% |   fyne.io/fyne/v2/internal/driver.walkObjectTree
         0     0% 97.88%     1.06MB  0.88%                | fyne.io/fyne/v2/internal/driver.walkObjectTree.func1
                                            1.06MB   100% |   fyne.io/fyne/v2/internal/driver.walkObjectTree
----------------------------------------------------------+-------------
                                            3.19MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).repaintWindow.func1
         0     0% 97.88%     3.19MB  2.65%                | fyne.io/fyne/v2/internal/driver/common.(*Canvas).FreeDirtyTextures
                                            3.19MB   100% |   fyne.io/fyne/v2/internal/async.(*CanvasObjectQueue).Out
----------------------------------------------------------+-------------
                                            3.50MB   100% |   fyne.io/fyne/v2/canvas.Refresh
         0     0% 97.88%     3.50MB  2.90%                | fyne.io/fyne/v2/internal/driver/common.(*Canvas).Refresh
                                               3MB 85.71% |   fyne.io/fyne/v2/internal/async.(*CanvasObjectQueue).In
                                            0.50MB 14.29% |   fyne.io/fyne/v2/internal/driver.walkObjectTree
----------------------------------------------------------+-------------
                                            1.06MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).repaintWindow.func1
         0     0% 97.88%     1.06MB  0.88%                | fyne.io/fyne/v2/internal/driver/common.(*Canvas).WalkTrees
                                            1.06MB   100% |   fyne.io/fyne/v2/internal/driver/common.(*Canvas).walkTree
----------------------------------------------------------+-------------
                                            1.06MB   100% |   fyne.io/fyne/v2/internal/driver/common.(*Canvas).WalkTrees
         0     0% 97.88%     1.06MB  0.88%                | fyne.io/fyne/v2/internal/driver/common.(*Canvas).walkTree
                                            1.06MB   100% |   fyne.io/fyne/v2/internal/driver.WalkVisibleObjectTree
----------------------------------------------------------+-------------
                                           13.14MB   100% |   fyne.io/fyne/v2.MeasureText
         0     0% 97.88%    13.14MB 10.89%                | fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).RenderedTextSize
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/painter.RenderedTextSize
----------------------------------------------------------+-------------
                                            3.70MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).startDrawThread.func1
         0     0% 97.88%     3.70MB  3.06%                | fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).drawSingleFrame
                                            3.70MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).repaintWindow
----------------------------------------------------------+-------------
                                            3.70MB 86.91% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).drawSingleFrame
                                            0.56MB 13.09% |   fyne.io/fyne/v2/internal/driver/glfw.(*window).RunWithContext
         0     0% 97.88%     4.25MB  3.52%                | fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).repaintWindow
                                            4.25MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*window).RunWithContext
----------------------------------------------------------+-------------
                                            4.25MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*window).RunWithContext
         0     0% 97.88%     4.25MB  3.52%                | fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).repaintWindow.func1
                                            3.19MB 75.11% |   fyne.io/fyne/v2/internal/driver/common.(*Canvas).FreeDirtyTextures
                                            1.06MB 24.89% |   fyne.io/fyne/v2/internal/driver/common.(*Canvas).WalkTrees
----------------------------------------------------------+-------------
         0     0% 97.88%     4.25MB  3.52%                | fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).startDrawThread.func1
                                            3.70MB 86.91% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).drawSingleFrame
                                            0.56MB 13.09% |   fyne.io/fyne/v2/internal/driver/glfw.(*window).RunWithContext
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*window).processResized
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/internal/driver/glfw.(*glCanvas).Resize
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*BaseWidget).Resize
----------------------------------------------------------+-------------
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*window).SetContent
         0     0% 97.88%    13.14MB 10.89%                | fyne.io/fyne/v2/internal/driver/glfw.(*glCanvas).SetContent
                                           13.14MB   100% |   fyne.io/fyne/v2/widget.(*BaseWidget).MinSize
----------------------------------------------------------+-------------
                                            4.25MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).repaintWindow
                                            0.56MB 13.09% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).startDrawThread.func1
         0     0% 97.88%     4.25MB  3.52%                | fyne.io/fyne/v2/internal/driver/glfw.(*window).RunWithContext
                                            4.25MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).repaintWindow.func1
                                            0.56MB 13.09% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).repaintWindow
----------------------------------------------------------+-------------
                                           13.14MB   100% |   main.main
         0     0% 97.88%    13.14MB 10.89%                | fyne.io/fyne/v2/internal/driver/glfw.(*window).SetContent
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*glCanvas).SetContent
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*window).resized
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/internal/driver/glfw.(*window).processResized
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*glCanvas).Resize
----------------------------------------------------------+-------------
                                            1.50MB   100% |   github.com/go-gl/glfw/v3.3/glfw.goWindowSizeCB
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/internal/driver/glfw.(*window).resized
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*window).processResized
----------------------------------------------------------+-------------
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/painter.measureText
         0     0% 97.88%    13.14MB 10.89%                | fyne.io/fyne/v2/internal/painter.CachedFontFace
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/painter.loadMeasureFont
----------------------------------------------------------+-------------
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).RenderedTextSize
         0     0% 97.88%    13.14MB 10.89%                | fyne.io/fyne/v2/internal/painter.RenderedTextSize
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/painter.measureText
----------------------------------------------------------+-------------
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/painter.CachedFontFace
         0     0% 97.88%    13.14MB 10.89%                | fyne.io/fyne/v2/internal/painter.loadMeasureFont
                                            8.85MB 67.31% |   github.com/go-text/typesetting/opentype/loader.(*Loader).findTableBuffer
                                            1.69MB 12.84% |   github.com/go-text/typesetting/opentype/api/font/cff.parseIndexContent
                                            1.55MB 11.81% |   github.com/go-text/typesetting/opentype/tables.ParseGlyf
                                            1.06MB  8.03% |   github.com/go-text/typesetting/opentype/tables.ParseBaseArray
----------------------------------------------------------+-------------
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/painter.RenderedTextSize
         0     0% 97.88%    13.14MB 10.89%                | fyne.io/fyne/v2/internal/painter.measureText
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/painter.CachedFontFace
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/widget.(*Scroll).Resize
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/internal/widget.(*Base).Resize
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/widget.(*scrollContainerRenderer).Layout
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*tableRenderer).Layout
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/internal/widget.(*Scroll).Resize
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/widget.(*Base).Resize
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/widget.(*Base).Resize
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/internal/widget.(*scrollContainerRenderer).Layout
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*tableCells).Resize
----------------------------------------------------------+-------------
                                           13.14MB 96.34% |   fyne.io/fyne/v2/internal/driver/glfw.(*glCanvas).SetContent
                                            0.50MB  3.66% |   fyne.io/fyne/v2/widget.(*Label).MinSize
         0     0% 97.88%    13.64MB 11.30%                | fyne.io/fyne/v2/widget.(*BaseWidget).MinSize
                                           13.64MB   100% |   fyne.io/fyne/v2/internal/cache.Renderer
----------------------------------------------------------+-------------
                                           36.51MB 35.80% |   fyne.io/fyne/v2/widget.(*tableRenderer).Refresh
                                            2.50MB  2.45% |   fyne.io/fyne/v2/widget.(*RichText).Refresh
                                            1.50MB  1.47% |   fyne.io/fyne/v2/widget.(*tableCells).Resize
                                            1.50MB  1.47% |   fyne.io/fyne/v2/widget.(*Label).Refresh
         0     0% 97.88%   101.97MB 84.48%                | fyne.io/fyne/v2/widget.(*BaseWidget).Refresh
                                          100.47MB 98.53% |   fyne.io/fyne/v2/widget.(*tableRenderer).Refresh
                                           38.01MB 37.27% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).Refresh
                                            3.50MB  3.43% |   fyne.io/fyne/v2/widget.(*textRenderer).Refresh
                                            0.50MB  0.49% |   fyne.io/fyne/v2/internal/cache.Renderer
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*glCanvas).Resize
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*Label).Resize
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/widget.(*BaseWidget).Resize
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/cache.Renderer
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*tableRenderer).Layout
----------------------------------------------------------+-------------
                                           88.66MB   100% |   fyne.io/fyne/v2/internal/cache.Renderer
         0     0% 97.88%    88.66MB 73.45%                | fyne.io/fyne/v2/widget.(*Label).CreateRenderer
                                           64.91MB 73.21% |   fyne.io/fyne/v2/widget.(*RichText).CreateRenderer
                                           23.75MB 26.79% |   fyne.io/fyne/v2/widget.NewRichTextWithText
----------------------------------------------------------+-------------
                                              27MB 29.00% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshHeaders
                                           26.96MB 28.95% |   fyne.io/fyne/v2/widget.(*Table).templateSize
                                              26MB 27.93% |   fyne.io/fyne/v2/widget.(*tableRenderer).Refresh
                                           13.14MB 14.12% |   fyne.io/fyne/v2/widget.(*Table).CreateRenderer
         0     0% 97.88%    93.11MB 77.14%                | fyne.io/fyne/v2/widget.(*Label).MinSize
                                           92.61MB 99.46% |   fyne.io/fyne/v2/internal/cache.Renderer
                                            0.50MB  0.54% |   fyne.io/fyne/v2/widget.(*BaseWidget).MinSize
----------------------------------------------------------+-------------
                                            4.50MB   100% |   fyne.io/fyne/v2/widget.(*Label).SetText
         0     0% 97.88%     4.50MB  3.73%                | fyne.io/fyne/v2/widget.(*Label).Refresh
                                               3MB 66.67% |   fyne.io/fyne/v2/widget.(*RichText).Refresh
                                            1.50MB 33.33% |   fyne.io/fyne/v2/widget.(*BaseWidget).Refresh
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshForID.func1
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/widget.(*Label).Resize
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*BaseWidget).Resize
----------------------------------------------------------+-------------
                                            4.50MB   100% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshForID
         0     0% 97.88%     4.50MB  3.73%                | fyne.io/fyne/v2/widget.(*Label).SetText
                                            4.50MB   100% |   fyne.io/fyne/v2/widget.(*Label).Refresh
----------------------------------------------------------+-------------
                                               3MB   100% |   fyne.io/fyne/v2/widget.(*Label).Refresh
         0     0% 97.88%        3MB  2.49%                | fyne.io/fyne/v2/widget.(*RichText).Refresh
                                            2.50MB 83.33% |   fyne.io/fyne/v2/widget.(*BaseWidget).Refresh
                                            0.50MB 16.67% |   fyne.io/fyne/v2/widget.(*RichText).updateRowBounds
----------------------------------------------------------+-------------
                                           12.90MB 65.65% |   fyne.io/fyne/v2/widget.(*RichText).CreateRenderer
                                            6.25MB 31.81% |   fyne.io/fyne/v2/widget.NewRichText
                                            0.50MB  2.55% |   fyne.io/fyne/v2/widget.(*RichText).Refresh
         0     0% 97.88%    19.64MB 16.27%                | fyne.io/fyne/v2/widget.(*RichText).updateRowBounds
                                           19.64MB   100% |   fyne.io/fyne/v2/widget.(*RichText).updateRowBounds.func1
----------------------------------------------------------+-------------
                                           13.14MB   100% |   fyne.io/fyne/v2/internal/cache.Renderer
         0     0% 97.88%    13.14MB 10.89%                | fyne.io/fyne/v2/widget.(*Table).CreateRenderer
                                           13.14MB   100% |   fyne.io/fyne/v2/widget.(*Label).MinSize
----------------------------------------------------------+-------------
                                               5MB 55.56% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshHeaders
                                               4MB 44.44% |   fyne.io/fyne/v2/widget.(*tableRenderer).Refresh
         0     0% 97.88%        9MB  7.46%                | fyne.io/fyne/v2/widget.(*Table).createHeader
                                               9MB   100% |   fyne.io/fyne/v2/widget.NewLabel
----------------------------------------------------------+-------------
                                           33.96MB   100% |   fyne.io/fyne/v2/widget.(*tableRenderer).Refresh
         0     0% 97.88%    33.96MB 28.14%                | fyne.io/fyne/v2/widget.(*Table).templateSize
                                           26.96MB 79.38% |   fyne.io/fyne/v2/widget.(*Label).MinSize
                                               7MB 20.62% |   fyne.io/fyne/v2/widget.NewLabel
----------------------------------------------------------+-------------
                                            2.50MB 83.33% |   fyne.io/fyne/v2/widget.(*textRenderer).Refresh
                                            0.50MB 16.67% |   fyne.io/fyne/v2/widget.(*TextSegment).Visual
         0     0% 97.88%        3MB  2.49%                | fyne.io/fyne/v2/widget.(*TextSegment).Update
                                            2.50MB 83.33% |   fyne.io/fyne/v2/canvas.(*Text).Refresh
----------------------------------------------------------+-------------
                                               9MB   100% |   fyne.io/fyne/v2/widget.(*RichText).cachedSegmentVisual
         0     0% 97.88%        9MB  7.46%                | fyne.io/fyne/v2/widget.(*TextSegment).Visual
                                            8.50MB 94.45% |   fyne.io/fyne/v2/canvas.NewText
                                            0.50MB  5.55% |   fyne.io/fyne/v2/widget.(*TextSegment).Update
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/widget.(*scrollContainerRenderer).Layout
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/widget.(*tableCells).Resize
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*BaseWidget).Refresh
----------------------------------------------------------+-------------
                                           38.01MB   100% |   fyne.io/fyne/v2/widget.(*BaseWidget).Refresh
         0     0% 97.88%    38.01MB 31.49%                | fyne.io/fyne/v2/widget.(*tableCellsRenderer).Refresh
                                           38.01MB   100% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshForID
----------------------------------------------------------+-------------
                                           38.01MB   100% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).Refresh
         0     0% 97.88%    38.01MB 31.49%                | fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshForID
                                           32.01MB 84.21% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshHeaders
                                            4.50MB 11.84% |   fyne.io/fyne/v2/widget.(*Label).SetText
                                            1.50MB  3.95% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshForID.func2
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshForID.func2
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshForID.func1
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*Label).Resize
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshForID
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshForID.func2
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshForID.func1
----------------------------------------------------------+-------------
                                           32.01MB   100% |   fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshForID
         0     0% 97.88%    32.01MB 26.51%                | fyne.io/fyne/v2/widget.(*tableCellsRenderer).refreshHeaders
                                              27MB 84.37% |   fyne.io/fyne/v2/widget.(*Label).MinSize
                                               5MB 15.63% |   fyne.io/fyne/v2/widget.(*Table).createHeader
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*BaseWidget).Resize
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/widget.(*tableRenderer).Layout
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/widget.(*Scroll).Resize
----------------------------------------------------------+-------------
                                          100.47MB   100% |   fyne.io/fyne/v2/widget.(*BaseWidget).Refresh
         0     0% 97.88%   100.47MB 83.24%                | fyne.io/fyne/v2/widget.(*tableRenderer).Refresh
                                           36.51MB 36.33% |   fyne.io/fyne/v2/widget.(*BaseWidget).Refresh
                                           33.96MB 33.80% |   fyne.io/fyne/v2/widget.(*Table).templateSize
                                              26MB 25.88% |   fyne.io/fyne/v2/widget.(*Label).MinSize
                                               4MB  3.98% |   fyne.io/fyne/v2/widget.(*Table).createHeader
----------------------------------------------------------+-------------
                                               9MB 56.25% |   fyne.io/fyne/v2/widget.(*Table).createHeader
                                               7MB 43.75% |   fyne.io/fyne/v2/widget.(*Table).templateSize
         0     0% 97.88%       16MB 13.26%                | fyne.io/fyne/v2/widget.NewLabel
                                              16MB   100% |   fyne.io/fyne/v2/widget.NewLabelWithStyle
----------------------------------------------------------+-------------
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.(*RichText).updateRowBounds.func1
         0     0% 97.88%     1.50MB  1.24%                | fyne.io/fyne/v2/widget.lineBounds
                                            1.50MB   100% |   fyne.io/fyne/v2/widget.splitLines
----------------------------------------------------------+-------------
         0     0% 97.88%        1MB  0.83%                | github.com/go-gl/glfw/v3.3/glfw.(*Window).SetSize
                                               1MB   100% |   github.com/go-gl/glfw/v3.3/glfw.(*Window).SetSize.func1
----------------------------------------------------------+-------------
                                               1MB 66.67% |   github.com/go-gl/glfw/v3.3/glfw.(*Window).SetSize
         0     0% 97.88%     1.50MB  1.24%                | github.com/go-gl/glfw/v3.3/glfw.(*Window).SetSize.func1
                                            1.50MB   100% |   github.com/go-gl/glfw/v3.3/glfw._Cfunc_glfwSetWindowSize
----------------------------------------------------------+-------------
                                            1.50MB   100% |   github.com/go-gl/glfw/v3.3/glfw.(*Window).SetSize.func1
         0     0% 97.88%     1.50MB  1.24%                | github.com/go-gl/glfw/v3.3/glfw._Cfunc_glfwSetWindowSize
                                            1.50MB   100% |   _cgoexp_16379c6f66fe_goWindowSizeCB
----------------------------------------------------------+-------------
                                            1.50MB   100% |   _cgoexp_16379c6f66fe_goWindowSizeCB
         0     0% 97.88%     1.50MB  1.24%                | github.com/go-gl/glfw/v3.3/glfw.goWindowSizeCB
                                            1.50MB   100% |   fyne.io/fyne/v2/internal/driver/glfw.(*window).resized
----------------------------------------------------------+-------------

Screenshots

No response

Example code

see how to reproduce.

Fyne version

2.4.5

Go compiler version

1.22.3

Operating system and version

macOS 13.1

Additional Information

No response

andydotxyz commented 5 months ago

Out of interest can you give it half a chance? Wait until the window is visible and also refresh at less than your screens refresh rate? For example a 100ms delay and then a 20ms ticker? I don't know if it will be resolved with that but it may be different as the renderers will be properly initialised once drawn.

ZSA233 commented 5 months ago

Out of interest can you give it half a chance? Wait until the window is visible and also refresh at less than your screens refresh rate? For example a 100ms delay and then a 20ms ticker? I don't know if it will be resolved with that but it may be different as the renderers will be properly initialised once drawn.

The testing code just a easy and simple way to reproduce the problem, not a regular scene. Actually I'm using it a very low refresh rate in production environment, such as 2 seconds refresh 4 * 100 cells, but it still take 100mb ram incr every 3 hours.

dweymouth commented 5 months ago

I think the table code could possibly be improved (looking quickly at the code I don't understand why we need to create a new template cell every refresh) but I don't believe there's a memory leak here. The renderer cache is periodically cleaned out to remove expired renderers for widgets that are not being drawn anymore, so while this will cause memory to go up for a bit, the renderers will eventually be GC'ed. What you may be seeing is Go holding on to more memory than is actually in use by the app too in case it needs to allocate more. Now running refresh every 10 ms will cause a lot of memory to accumulate because IIRC the caches are cleaned every 1 or 2 minutes.

dweymouth commented 5 months ago

Another thing @ZSA233 if you're updating a table and refreshing for each cell (not sure if you're doing this) - it's better to update all the cells and then refresh the table once. (That is assuming you update all the cells together). In general it's important for performance in Fyne to refresh only when needed, and at the lowest refresh scope too. Basically don't refresh until you want the widget to be redrawn.

ZSA233 commented 5 months ago

I think the table code could possibly be improved (looking quickly at the code I don't understand why we need to create a new template cell every refresh) but I don't believe there's a memory leak here. The renderer cache is periodically cleaned out to remove expired renderers for widgets that are not being drawn anymore, so while this will cause memory to go up for a bit, the renderers will eventually be GC'ed. What you may be seeing is Go holding on to more memory than is actually in use by the app too in case it needs to allocate more. Now running refresh every 10 ms will cause a lot of memory to accumulate because IIRC the caches are cleaned every 1 or 2 minutes.

You are right for the cache cleaning code in the driver, so cache.Renderer() may not be the reason for the memory leak. However, the leak still exists for somewhere, as it persists even after several hours of executing table.Refresh(), even at a low refresh rate.

andydotxyz commented 5 months ago

This could be a duplicate of text cache issues we are working on, if the text in the cells is changing on each refresh. I was perhaps diverted from thinking this based on the assertion that it was the create calls being over used that was the source of the issue. On further investigation does it seem like the memory rather than the function calls is the issue being investigated?

dweymouth commented 5 months ago

Could be, though I still don't think it's an actual memory leak, as the text texture cache is cleaned out on a schedule too. It's an issue of more memory being consumed than needed though since each unique line of text (as opposed to glyph) shown is a unique entry in the texture cache.

andydotxyz commented 5 months ago

It shouldn't grow like the OP is reporting though surely - over hours of usage?

ZSA233 commented 5 months ago

It shouldn't grow like the OP is reporting though surely - over hours of usage?

Yeap, the OP's testing code may be incorrect for reproducing my prod env problem. I have been re-testing the OP code for over an hour, the memory usage has been stable, running between 100~200MB, which is not a significant issue. Therefore, I make another test that is closer to the memory issues in prod env:

testing result

  1. Time: Jun 7, 2024 at 1:03am (CST)
    
    PS C:\Users\ZSA> go tool pprof http://127.0.0.1:9996/debug/pprof/heap
    Fetching profile over HTTP from http://127.0.0.1:9996/debug/pprof/heap
    Saved profile in C:\Users\ZSA\pprof\pprof.___6go_build_test.exe.alloc_objects.alloc_space.inuse_objects.inuse_space.001.pb.gz
    File: ___6go_build_test.exe
    Build ID: C:\Users\ZSA\AppData\Local\JetBrains\GoLand2024.1\tmp\GoLand\___6go_build_test.exe2024-06-07 00:59:15.714434 +0800 CST
    Type: inuse_space
    Time: Jun 7, 2024 at 1:03am (CST)
    Entering interactive mode (type "help" for commands, "o" for options)
    (pprof) top20
    Showing nodes accounting for 23784.30kB, 100% of 23784.30kB total
    Showing top 20 nodes out of 131
      flat  flat%   sum%        cum   cum%
    9605.13kB 40.38% 40.38%  9605.13kB 40.38%  github.com/go-text/typesetting/opentype/loader.(*Loader).findTableBuffer
    2560.70kB 10.77% 51.15%  2560.70kB 10.77%  fyne.io/fyne/v2/widget.(*RichText).cachedSegmentVisual
    2272.37kB  9.55% 60.70%  2272.37kB  9.55%  fyne.io/fyne/v2/internal/cache.SetFontMetrics
    1056.33kB  4.44% 65.15%  1056.33kB  4.44%  github.com/go-text/typesetting/opentype/tables.ParseBaseArray
    1024.20kB  4.31% 69.45%  1024.20kB  4.31%  fyne.io/fyne/v2/widget.NewLabelWithStyle
    1024.19kB  4.31% 73.76%  8044.71kB 33.82%  fyne.io/fyne/v2/widget.NewRichText (inline)
    1024.06kB  4.31% 78.06%  2048.21kB  8.61%  github.com/go-text/typesetting/opentype/tables.(*Glyph).parseData
    596.16kB  2.51% 80.57%   596.16kB  2.51%  github.com/go-text/typesetting/opentype/api/font/cff.parseIndexContent
    522.70kB  2.20% 82.77%   522.70kB  2.20%  github.com/go-text/typesetting/unicodedata.map.init.0
    513.50kB  2.16% 84.93%   513.50kB  2.16%  image.NewRGBA
    512.44kB  2.15% 87.08%   512.44kB  2.15%  github.com/go-text/typesetting/harfbuzz.newShaperOpentype
    512.17kB  2.15% 89.24%   512.17kB  2.15%  github.com/go-text/typesetting/opentype/api/font.(*Face).getPointsForGlyph
    512.12kB  2.15% 91.39%   512.12kB  2.15%  github.com/go-text/typesetting/harfbuzz.(*otMap).addLookups
    512.10kB  2.15% 93.54%   512.10kB  2.15%  github.com/go-text/typesetting/opentype/tables.(*SimpleGlyph).parsePoints
    512.05kB  2.15% 95.69%   512.05kB  2.15%  regexp/syntax.(*parser).newRegexp
    512.05kB  2.15% 97.85%   512.05kB  2.15%  github.com/go-text/typesetting/opentype/tables.(*CompositeGlyph).parseGlyphs  512.02kB  2.15%   100%   512.02kB  2.15%  fyne.io/fyne/v2/internal/cache.SetCanvasForObject
         0     0%   100% 15578.21kB 65.50%  fyne.io/fyne/v2.MeasureText
         0     0%   100% 16890.72kB 71.02%  fyne.io/fyne/v2/internal/cache.Renderer
         0     0%   100%  2562.25kB 10.77%  fyne.io/fyne/v2/internal/driver.WalkVisibleObjectTree
    (pprof)
    PS C:\Users\ZSA> tasklist /FI "PID eq 11876"

映像名称 PID 会话名 会话# 内存使用 ========================= ======== ================ =========== ============ ___6go_build_test.exe 11876 Console 1 155,352 K


2. Time: Jun 7, 2024 at 11:56am (CST)
```shell
PS C:\Users\ZSA> go tool pprof http://127.0.0.1:9996/debug/pprof/heap
Fetching profile over HTTP from http://127.0.0.1:9996/debug/pprof/heap
Saved profile in C:\Users\ZSA\pprof\pprof.___6go_build_test.exe.alloc_objects.alloc_space.inuse_objects.inuse_space.005.pb.gz
File: ___6go_build_test.exe
Build ID: C:\Users\ZSA\AppData\Local\JetBrains\GoLand2024.1\tmp\GoLand\___6go_build_test.exe2024-06-07 00:59:15.714434 +0800 CST
Type: inuse_space
Time: Jun 7, 2024 at 11:56am (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top20
Showing nodes accounting for 440.77MB, 96.11% of 458.63MB total
Dropped 86 nodes (cum <= 2.29MB)
Showing top 20 nodes out of 82
      flat  flat%   sum%        cum   cum%
  135.54MB 29.55% 29.55%   173.04MB 37.73%  fyne.io/fyne/v2/widget.(*RichText).cachedSegmentVisual
   69.51MB 15.16% 44.71%    69.51MB 15.16%  fyne.io/fyne/v2/widget.NewLabelWithStyle (inline)
   55.01MB 11.99% 56.70%    61.87MB 13.49%  fyne.io/fyne/v2/widget.NewRichText (inline)
      36MB  7.85% 64.55%       36MB  7.85%  fyne.io/fyne/v2/canvas.NewText
      32MB  6.98% 71.53%    93.87MB 20.47%  fyne.io/fyne/v2/widget.NewRichTextWithText
      25MB  5.45% 76.98%       25MB  5.45%  fyne.io/fyne/v2/canvas.NewRectangle (inline)
   22.50MB  4.91% 81.89%    43.71MB  9.53%  fyne.io/fyne/v2/widget.(*RichText).updateRowBounds.func1
   21.81MB  4.76% 86.65%   370.87MB 80.86%  fyne.io/fyne/v2/internal/cache.Renderer
   10.50MB  2.29% 88.94%   250.69MB 54.66%  fyne.io/fyne/v2/widget.(*RichText).CreateRenderer
    9.38MB  2.05% 90.98%     9.38MB  2.05%  github.com/go-text/typesetting/opentype/loader.(*Loader).findTableBuffer
       6MB  1.31% 92.29%        6MB  1.31%  fyne.io/fyne/v2/widget.splitLines
    5.50MB  1.20% 93.49%   181.54MB 39.58%  fyne.io/fyne/v2/widget.(*textRenderer).Refresh
    4.50MB  0.98% 94.47%     4.50MB  0.98%  fyne.io/fyne/v2/internal/cache.(*expiringCache).setAlive
       4MB  0.87% 95.34%        4MB  0.87%  fyne.io/fyne/v2/theme.darkPaletColorNamed
       3MB  0.65% 96.00%     4.50MB  0.98%  github.com/go-text/typesetting/harfbuzz.NewFont
    0.50MB  0.11% 96.11%     8.02MB  1.75%  fyne.io/fyne/v2/internal/driver/glfw.(*gLDriver).startDrawThread.func1
         0     0% 96.11%    15.21MB  3.32%  fyne.io/fyne/v2.MeasureText
         0     0% 96.11%    13.52MB  2.95%  fyne.io/fyne/v2/internal/driver.WalkVisibleObjectTree
         0     0% 96.11%    13.52MB  2.95%  fyne.io/fyne/v2/internal/driver.walkObjectTree
         0     0% 96.11%    13.52MB  2.95%  fyne.io/fyne/v2/internal/driver.walkObjectTree.func1 (inline)
(pprof)
PS C:\Users\ZSA> tasklist /FI "PID eq 11876"

映像名称                       PID 会话名              会话#       内存使用
========================= ======== ================ =========== ============
___6go_build_test.exe        11876 Console                    1  1,009,812 K
  1. 10 hours after, the memory usage increased from 155,352 K to 1,009,812 K.

testing code

main.go

main.go
package main
import (
    "fmt"
    "fyne.io/fyne/v2"
    "fyne.io/fyne/v2/app"
    "strconv"
    "time"
)
var rows = [][]string{}
func main() {
    myApp := app.New()
    myWindow := myApp.NewWindow("test")
    myWindow.Resize(fyne.NewSize(1000, 500))
    headers := []string{}
    for i := 0; i < 100; i++ {
       rows = append(rows, make([]string, 100))
       headers = append(headers, "#"+strconv.Itoa(i))
       for j := 0; j < 100; j++ {
          rows[i][j] = fmt.Sprintf("%d:%d", i, j)
       }
    }
    table := NewTableEx(headers)
    myWindow.SetContent(table)
    go runPprof()
    go refreshLoop(table)
    myWindow.ShowAndRun()
}
func refreshLoop(table *TableEx) {
    tick := time.NewTicker(time.Millisecond * 500)
    defer tick.Stop()
    for _ = range tick.C {
       for i := 0; i < 100; i++ {
          for j := 0; j < 100; j++ {
             rows[i][j] = fmt.Sprintf("%d:%d", time.Now().Second()+i, time.Now().Second()+j)
          }
       }
       table.SetMatrix(rows)
    }
}

table.go

package main
import (
    "fyne.io/fyne/v2"
    "fyne.io/fyne/v2/widget"
    "strconv"
    "sync/atomic"
)
type TableEx struct {
    *widget.Table
    header atomic.Pointer[[]string]
    rows   atomic.Pointer[[][]string]
}
func (w *TableEx) Resize(size fyne.Size) {
    w.Table.Resize(size)
}
func (w *TableEx) SetMatrix(ss [][]string) {
    w.rows.Store(&ss)
    w.Table.Refresh()
}
func NewTableEx(header []string) *TableEx {
    table := widget.NewTableWithHeaders(nil, nil, nil)
    table.ShowHeaderRow = true
    table.ShowHeaderColumn = true
    var hlp *TableEx
    hlp = &TableEx{
       header: atomic.Pointer[[]string]{},
       rows:   atomic.Pointer[[][]string]{},
       Table:  table,
    }
    hlp.header.Store(&header)
    hlp.rows.Store(&[][]string{})
    table.Length = func() (rows int, cols int) {
       return len(*hlp.rows.Load()), len(*hlp.header.Load())
    }
    table.CreateCell = func() fyne.CanvasObject {
       return widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{})
    }
    table.CreateHeader = func() fyne.CanvasObject {
       return widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{
          Bold: true,
       })
    }
    table.UpdateCell = func(id widget.TableCellID, template fyne.CanvasObject) {
       l := template.(*widget.Label)
       var text string
       rows := *hlp.rows.Load()
       if id.Row < len(rows) && len(rows[id.Row]) > id.Col {
          text = rows[id.Row][id.Col]
       }
       l.SetText(text)
    }
    table.UpdateHeader = func(id widget.TableCellID, template fyne.CanvasObject) {
       l := template.(*widget.Label)
       if id.Row < 0 {
          h := *hlp.header.Load()
          if id.Col >= len(h) {
          } else {
             l.SetText(h[id.Col])
          }
       } else if id.Col < 0 {
          l.SetText(strconv.Itoa(id.Row + 1))
          if l.Alignment != fyne.TextAlignTrailing {
             l.Alignment = fyne.TextAlignTrailing
          }
       } else {
          return
       }
    }
    return hlp
}

debug.go

package main
import (
    "net/http"
    "net/http/pprof"
)
func runPprof() {
    //http.HandleFunc("/debug/pprof/", pprof.Index)
    //http.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
    //http.HandleFunc("/debug/pprof/profile", pprof.Profile)
    //http.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
    //http.HandleFunc("/debug/pprof/trace", pprof.Trace)
    http.HandleFunc("/debug/pprof/allocs", pprof.Handler("allocs").ServeHTTP)
    http.HandleFunc("/debug/pprof/block", pprof.Handler("block").ServeHTTP)
    http.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
    http.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
    http.HandleFunc("/debug/pprof/mutex", pprof.Handler("mutex").ServeHTTP)
    http.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
    http.ListenAndServe("0.0.0.0:9996", nil)
}
ZSA233 commented 5 months ago
dweymouth commented 5 months ago

If there is indeed a leak, I imagine it must be in GPU textures, as I'm less familiar with how that part of the codebase works and I'm pretty sure there's no leak on the widget/renderer side of things and awhile back did a bit of a codebase audit looking for leaks, fixed a few, but didn't find any in this area.

Or else it's Go holding on to memory because the OS isn't under memory pressure and asking for it back. AFAIK, the Go runtime marks freed memory as "unneeded" on most OSes but retains it, or at least a chunk of it, unless the OS asks for it back, to speed future allocations.

Jacalz commented 5 months ago

Or else it's Go holding on to memory because the OS isn't under memory pressure and asking for it back. AFAIK, the Go runtime marks freed memory as "unneeded" on most OSes but retains it, or at least a chunk of it, unless the OS asks for it back, to speed future allocations.

I’m no pprof magician but it feels like the built-in profiling in Go should report actual me memory usage and not how much it is holding on to? My best bet is that this is an actual leak somewhere.

andydotxyz commented 5 months ago

From what I can see TableEx is not extending correctly.

type TableEx struct {
    *widget.Table

should be:

type TableEx struct {
    widget.Table

and there is no call to ExtendBaseWidget.

If the code will not produce the same leak with the regular widget.Table then I would say this may not be a leak in the toolkit directly.

Keima-lpj commented 2 months ago

I'm having a similar problem, once per second refresh the table and the memory will grow fast, about 1M a second. after some time passes, a gc will be triggered and the memory will be reduced from 200M to 50M. then it will grow slowly again. Much slower than the initial growth.

first time: 4623409138

the other time: 4678194878

andydotxyz commented 2 months ago

We cache all the graphical textures, which includes text rendered. When nothing has been shown the cache will grow fast. Then we clean out unused textures (approx each minute) and re-used items will stay in memory so growth later on will indeed be slower.

I'm not sure that this indicates a memory leak as per the original post.

knusbaum commented 2 months ago

@andydotxyz I ran into this issue, and there does appear to be a leak of sorts.

In the current glfw driver, the cache never gets cleaned. I looked at the mobile driver and did more or less what it was doing and was able to fix my issue, and also fix the issue in the testcase code in https://github.com/fyne-io/fyne/issues/4903#issuecomment-2153844849

My PR: https://github.com/fyne-io/fyne/pull/5112

@ZSA233 are you able to see if this fixes your issue?

As a side note, I noticed two things: