Answering my own question:
If a JTable is constructed with a TableModel but without a TableColumnModel the JTable will create a TableColumnModel using createDefaultColumnModel() and set autoCreateColumnsFromModel to true. When this property is true, the JTable will populate the TableColumnModel with values from the TableModel.
No one seems to guarantee that the two are kept in sync. Case in point, JTable.getColumnName() will return the TableModel column name regardless of what the TableColumnModel actually displays on the screen.
Another interesting thing I noticed is that TableModel is limited to String columns whereas TableColumnModel allows you to pass any Object to the TableCellRenderer. The Javadoc says that the values are restricted to Strings but in fact this is implementation-specific. Nothing prevents you from writing an implementation that uses a JComponent value.
In summary: TableColumnModel is the ultimate owner of column values. TableColumnModel only asks TableModel for values only if it doesn't already have one. For example, in the case where you pass a column into JTable.addColumn() without specifying a header value.